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

r875:567f1ebe063b - - 8 files changed: 80 inserted, 78 deleted

@@ -1,205 +1,206
1 require 'csv'
1 require 'csv'
2
2
3 class UserAdminController < ApplicationController
3 class UserAdminController < ApplicationController
4
4
5 include MailHelperMethods
5 include MailHelperMethods
6
6
7 before_action :admin_authorization
7 before_action :admin_authorization
8
8
9 def index
9 def index
10 @user_count = User.count
10 @user_count = User.count
11 @users = User.all
11 @users = User.all
12 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
12 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
13 @contests = Contest.enabled
13 @contests = Contest.enabled
14 + @user = User.new
14 end
15 end
15
16
16 def active
17 def active
17 sessions = ActiveRecord::SessionStore::Session.where("updated_at >= ?", 60.minutes.ago)
18 sessions = ActiveRecord::SessionStore::Session.where("updated_at >= ?", 60.minutes.ago)
18 @users = []
19 @users = []
19 sessions.each do |session|
20 sessions.each do |session|
20 if session.data[:user_id]
21 if session.data[:user_id]
21 @users << User.find(session.data[:user_id])
22 @users << User.find(session.data[:user_id])
22 end
23 end
23 end
24 end
24 end
25 end
25
26
26 def show
27 def show
27 @user = User.find(params[:id])
28 @user = User.find(params[:id])
28 end
29 end
29
30
30 def new
31 def new
31 @user = User.new
32 @user = User.new
32 end
33 end
33
34
34 def create
35 def create
35 @user = User.new(user_params)
36 @user = User.new(user_params)
36 @user.activated = true
37 @user.activated = true
37 if @user.save
38 if @user.save
38 flash[:notice] = 'User was successfully created.'
39 flash[:notice] = 'User was successfully created.'
39 redirect_to :action => 'index'
40 redirect_to :action => 'index'
40 else
41 else
41 render :action => 'new'
42 render :action => 'new'
42 end
43 end
43 end
44 end
44
45
45 def clear_last_ip
46 def clear_last_ip
46 @user = User.find(params[:id])
47 @user = User.find(params[:id])
47 @user.last_ip = nil
48 @user.last_ip = nil
48 @user.save
49 @user.save
49 redirect_to action: 'index', page: params[:page]
50 redirect_to action: 'index', page: params[:page]
50 end
51 end
51
52
52 def create_from_list
53 def create_from_list
53 lines = params[:user_list]
54 lines = params[:user_list]
54
55
55
56
56 res = User.create_from_list(lines)
57 res = User.create_from_list(lines)
57 error_logins = res[:error_logins]
58 error_logins = res[:error_logins]
58 error_msg = res[:first_error]
59 error_msg = res[:first_error]
59 ok_user = res[:created_users]
60 ok_user = res[:created_users]
60
61
61
62
62 #add to group
63 #add to group
63 if params[:add_to_group]
64 if params[:add_to_group]
64 group = Group.find_by(id: params[:group_id])&.add_users_skip_existing(ok_user)
65 group = Group.find_by(id: params[:group_id])&.add_users_skip_existing(ok_user)
65 end
66 end
66
67
67 # show flash
68 # show flash
68 if ok_user.count > 0
69 if ok_user.count > 0
69 flash[:success] = "#{ok_user.count} user(s) was created or updated successfully"
70 flash[:success] = "#{ok_user.count} user(s) was created or updated successfully"
70 end
71 end
71 if error_logins.size > 0
72 if error_logins.size > 0
72 flash[:error] = "Following user(s) failed to be created: " + error_logins.join(', ') + ". The error of the first failed one are: " + error_msg;
73 flash[:error] = "Following user(s) failed to be created: " + error_logins.join(', ') + ". The error of the first failed one are: " + error_msg;
73 end
74 end
74 redirect_to :action => 'index'
75 redirect_to :action => 'index'
75 end
76 end
76
77
77 def edit
78 def edit
78 @user = User.find(params[:id])
79 @user = User.find(params[:id])
79 end
80 end
80
81
81 def update
82 def update
82 @user = User.find(params[:id])
83 @user = User.find(params[:id])
83 if @user.update_attributes(user_params)
84 if @user.update_attributes(user_params)
84 flash[:notice] = 'User was successfully updated.'
85 flash[:notice] = 'User was successfully updated.'
85 redirect_to :action => 'show', :id => @user
86 redirect_to :action => 'show', :id => @user
86 else
87 else
87 render :action => 'edit'
88 render :action => 'edit'
88 end
89 end
89 end
90 end
90
91
91 def destroy
92 def destroy
92 User.find(params[:id]).destroy
93 User.find(params[:id]).destroy
93 redirect_to :action => 'index'
94 redirect_to :action => 'index'
94 end
95 end
95
96
96 def user_stat
97 def user_stat
97 if params[:commit] == 'download csv'
98 if params[:commit] == 'download csv'
98 @problems = Problem.all
99 @problems = Problem.all
99 else
100 else
100 @problems = Problem.available_problems
101 @problems = Problem.available_problems
101 end
102 end
102 @users = User.includes(:contests, :contest_stat).where(enabled: true)
103 @users = User.includes(:contests, :contest_stat).where(enabled: true)
103 @scorearray = Array.new
104 @scorearray = Array.new
104 @users.each do |u|
105 @users.each do |u|
105 ustat = Array.new
106 ustat = Array.new
106 ustat[0] = u
107 ustat[0] = u
107 @problems.each do |p|
108 @problems.each do |p|
108 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
109 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
109 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
110 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
110 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
111 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
111 else
112 else
112 ustat << [0,false]
113 ustat << [0,false]
113 end
114 end
114 end
115 end
115 @scorearray << ustat
116 @scorearray << ustat
116 end
117 end
117 if params[:commit] == 'download csv' then
118 if params[:commit] == 'download csv' then
118 csv = gen_csv_from_scorearray(@scorearray,@problems)
119 csv = gen_csv_from_scorearray(@scorearray,@problems)
119 send_data csv, filename: 'last_score.csv'
120 send_data csv, filename: 'last_score.csv'
120 else
121 else
121 render template: 'user_admin/user_stat'
122 render template: 'user_admin/user_stat'
122 end
123 end
123 end
124 end
124
125
125 def user_stat_max
126 def user_stat_max
126 if params[:commit] == 'download csv'
127 if params[:commit] == 'download csv'
127 @problems = Problem.all
128 @problems = Problem.all
128 else
129 else
129 @problems = Problem.available_problems
130 @problems = Problem.available_problems
130 end
131 end
131 @users = User.includes(:contests).includes(:contest_stat).all
132 @users = User.includes(:contests).includes(:contest_stat).all
132 @scorearray = Array.new
133 @scorearray = Array.new
133 #set up range from param
134 #set up range from param
134 since_id = params.fetch(:since_id, 0).to_i
135 since_id = params.fetch(:since_id, 0).to_i
135 until_id = params.fetch(:until_id, 0).to_i
136 until_id = params.fetch(:until_id, 0).to_i
136 @users.each do |u|
137 @users.each do |u|
137 ustat = Array.new
138 ustat = Array.new
138 ustat[0] = u
139 ustat[0] = u
139 @problems.each do |p|
140 @problems.each do |p|
140 max_points = 0
141 max_points = 0
141 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
142 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
142 max_points = sub.points if sub and sub.points and (sub.points > max_points)
143 max_points = sub.points if sub and sub.points and (sub.points > max_points)
143 end
144 end
144 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
145 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
145 end
146 end
146 @scorearray << ustat
147 @scorearray << ustat
147 end
148 end
148
149
149 if params[:commit] == 'download csv' then
150 if params[:commit] == 'download csv' then
150 csv = gen_csv_from_scorearray(@scorearray,@problems)
151 csv = gen_csv_from_scorearray(@scorearray,@problems)
151 send_data csv, filename: 'max_score.csv'
152 send_data csv, filename: 'max_score.csv'
152 else
153 else
153 render template: 'user_admin/user_stat'
154 render template: 'user_admin/user_stat'
154 end
155 end
155 end
156 end
156
157
157 def import
158 def import
158 if params[:file]==''
159 if params[:file]==''
159 flash[:notice] = 'Error importing no file'
160 flash[:notice] = 'Error importing no file'
160 redirect_to :action => 'index' and return
161 redirect_to :action => 'index' and return
161 end
162 end
162 import_from_file(params[:file])
163 import_from_file(params[:file])
163 end
164 end
164
165
165 def random_all_passwords
166 def random_all_passwords
166 users = User.all
167 users = User.all
167 @prefix = params[:prefix] || ''
168 @prefix = params[:prefix] || ''
168 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
169 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
169 @changed = false
170 @changed = false
170 if params[:commit] == 'Go ahead'
171 if params[:commit] == 'Go ahead'
171 @non_admin_users.each do |user|
172 @non_admin_users.each do |user|
172 password = random_password
173 password = random_password
173 user.password = password
174 user.password = password
174 user.password_confirmation = password
175 user.password_confirmation = password
175 user.save
176 user.save
176 end
177 end
177 @changed = true
178 @changed = true
178 end
179 end
179 end
180 end
180
181
181 # contest management
182 # contest management
182
183
183 def contests
184 def contests
184 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
185 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
185 @contests = Contest.enabled
186 @contests = Contest.enabled
186 end
187 end
187
188
188 def assign_from_list
189 def assign_from_list
189 contest_id = params[:users_contest_id]
190 contest_id = params[:users_contest_id]
190 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
191 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
191 contest = Contest.find(params[:new_contest][:id])
192 contest = Contest.find(params[:new_contest][:id])
192 if !contest
193 if !contest
193 flash[:notice] = 'Error: no contest'
194 flash[:notice] = 'Error: no contest'
194 redirect_to :action => 'contests', :id =>contest_id
195 redirect_to :action => 'contests', :id =>contest_id
195 end
196 end
196
197
197 note = []
198 note = []
198 users.each do |u|
199 users.each do |u|
199 u.contests = [contest]
200 u.contests = [contest]
200 note << u.login
201 note << u.login
201 end
202 end
202 flash[:notice] = 'User(s) ' + note.join(', ') +
203 flash[:notice] = 'User(s) ' + note.join(', ') +
203 " were successfully reassigned to #{contest.title}."
204 " were successfully reassigned to #{contest.title}."
204 redirect_to :action => 'contests', :id =>contest.id
205 redirect_to :action => 'contests', :id =>contest.id
205 end
206 end
@@ -20,203 +20,193
20 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
20 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
21 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'list', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
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 if GraderConfiguration['system.user_setting_enabled']
22 if GraderConfiguration['system.user_setting_enabled']
23 right_menu << add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog')}".html_safe, 'users', 'index', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
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 end
24 end
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'}})
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 result = content_tag(:ul,left_menu.html_safe,class: 'nav navbar-nav') + content_tag(:ul,right_menu.html_safe,class: 'nav navbar-nav navbar-right')
28 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 end
29 end
30
30
31 def add_menu(title, controller, action, html_option = {})
31 def add_menu(title, controller, action, html_option = {})
32 link_option = {controller: controller, action: action}
32 link_option = {controller: controller, action: action}
33 html_option[:class] = (html_option[:class] || '') + " active" if current_page?(link_option)
33 html_option[:class] = (html_option[:class] || '') + " active" if current_page?(link_option)
34 content_tag(:li, link_to(title,link_option),html_option)
34 content_tag(:li, link_to(title,link_option),html_option)
35 end
35 end
36
36
37 def user_header
37 def user_header
38 menu_items = ''
38 menu_items = ''
39 user = User.find(session[:user_id])
39 user = User.find(session[:user_id])
40
40
41 if (user!=nil) and (session[:admin])
41 if (user!=nil) and (session[:admin])
42 # admin menu
42 # admin menu
43 menu_items << "<b>Administrative task:</b> "
43 menu_items << "<b>Administrative task:</b> "
44 append_to menu_items, '[Announcements]', 'announcements', 'index'
44 append_to menu_items, '[Announcements]', 'announcements', 'index'
45 append_to menu_items, '[Msg console]', 'messages', 'console'
45 append_to menu_items, '[Msg console]', 'messages', 'console'
46 append_to menu_items, '[Problems]', 'problems', 'index'
46 append_to menu_items, '[Problems]', 'problems', 'index'
47 append_to menu_items, '[Users]', 'user_admin', 'index'
47 append_to menu_items, '[Users]', 'user_admin', 'index'
48 append_to menu_items, '[Results]', 'user_admin', 'user_stat'
48 append_to menu_items, '[Results]', 'user_admin', 'user_stat'
49 append_to menu_items, '[Report]', 'report', 'multiple_login'
49 append_to menu_items, '[Report]', 'report', 'multiple_login'
50 append_to menu_items, '[Graders]', 'graders', 'list'
50 append_to menu_items, '[Graders]', 'graders', 'list'
51 append_to menu_items, '[Contests]', 'contest_management', 'index'
51 append_to menu_items, '[Contests]', 'contest_management', 'index'
52 append_to menu_items, '[Sites]', 'sites', 'index'
52 append_to menu_items, '[Sites]', 'sites', 'index'
53 append_to menu_items, '[System config]', 'configurations', 'index'
53 append_to menu_items, '[System config]', 'configurations', 'index'
54 menu_items << "<br/>"
54 menu_items << "<br/>"
55 end
55 end
56
56
57 # main page
57 # main page
58 append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list'
58 append_to menu_items, "[#{I18n.t 'menu.main'}]", 'main', 'list'
59 append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list'
59 append_to menu_items, "[#{I18n.t 'menu.messages'}]", 'messages', 'list'
60
60
61 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
61 if (user!=nil) and (GraderConfiguration.show_tasks_to?(user))
62 append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list'
62 append_to menu_items, "[#{I18n.t 'menu.tasks'}]", 'tasks', 'list'
63 append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission'
63 append_to menu_items, "[#{I18n.t 'menu.submissions'}]", 'main', 'submission'
64 append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index'
64 append_to menu_items, "[#{I18n.t 'menu.test'}]", 'test', 'index'
65 end
65 end
66
66
67 if GraderConfiguration['right.user_hall_of_fame']
67 if GraderConfiguration['right.user_hall_of_fame']
68 append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof'
68 append_to menu_items, "[#{I18n.t 'menu.hall_of_fame'}]", 'report', 'problem_hof'
69 end
69 end
70 append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help'
70 append_to menu_items, "[#{I18n.t 'menu.help'}]", 'main', 'help'
71
71
72 if GraderConfiguration['system.user_setting_enabled']
72 if GraderConfiguration['system.user_setting_enabled']
73 append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index'
73 append_to menu_items, "[#{I18n.t 'menu.settings'}]", 'users', 'index'
74 end
74 end
75 append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login'
75 append_to menu_items, "[#{I18n.t 'menu.log_out'}]", 'main', 'login'
76
76
77 menu_items.html_safe
77 menu_items.html_safe
78 end
78 end
79
79
80 def append_to(option,label, controller, action)
80 def append_to(option,label, controller, action)
81 option << ' ' if option!=''
81 option << ' ' if option!=''
82 option << link_to_unless_current(label,
82 option << link_to_unless_current(label,
83 :controller => controller,
83 :controller => controller,
84 :action => action)
84 :action => action)
85 end
85 end
86
86
87 def format_short_time(time)
87 def format_short_time(time)
88 now = Time.zone.now
88 now = Time.zone.now
89 st = ''
89 st = ''
90 if (time.yday != now.yday) or (time.year != now.year)
90 if (time.yday != now.yday) or (time.year != now.year)
91 st = time.strftime("%d/%m/%y ")
91 st = time.strftime("%d/%m/%y ")
92 end
92 end
93 st + time.strftime("%X")
93 st + time.strftime("%X")
94 end
94 end
95
95
96 def format_short_duration(duration)
96 def format_short_duration(duration)
97 return '' if duration==nil
97 return '' if duration==nil
98 d = duration.to_f
98 d = duration.to_f
99 return Time.at(d).gmtime.strftime("%X")
99 return Time.at(d).gmtime.strftime("%X")
100 end
100 end
101
101
102 def format_full_time_ago(time)
102 def format_full_time_ago(time)
103 st = time_ago_in_words(time) + ' ago (' + format_short_time(time) + ')'
103 st = time_ago_in_words(time) + ' ago (' + format_short_time(time) + ')'
104 end
104 end
105
105
106 def read_textfile(fname,max_size=2048)
106 def read_textfile(fname,max_size=2048)
107 begin
107 begin
108 File.open(fname).read(max_size)
108 File.open(fname).read(max_size)
109 rescue
109 rescue
110 nil
110 nil
111 end
111 end
112 end
112 end
113
113
114 def toggle_button(on,toggle_url,id, option={})
114 def toggle_button(on,toggle_url,id, option={})
115 btn_size = option[:size] || 'btn-sm'
115 btn_size = option[:size] || 'btn-sm'
116 btn_block = option[:block] || 'btn-block'
116 btn_block = option[:block] || 'btn-block'
117 link_to (on ? "Yes" : "No"), toggle_url,
117 link_to (on ? "Yes" : "No"), toggle_url,
118 {class: "btn #{btn_block} #{btn_size} btn-#{on ? 'success' : 'outline-secondary'} ajax-toggle",
118 {class: "btn #{btn_block} #{btn_size} btn-#{on ? 'success' : 'outline-secondary'} ajax-toggle",
119 id: id,
119 id: id,
120 data: {remote: true, method: 'get'}}
120 data: {remote: true, method: 'get'}}
121 end
121 end
122
122
123 def get_ace_mode(language)
123 def get_ace_mode(language)
124 # return ace mode string from Language
124 # return ace mode string from Language
125
125
126 case language.pretty_name
126 case language.pretty_name
127 when 'Pascal'
127 when 'Pascal'
128 'ace/mode/pascal'
128 'ace/mode/pascal'
129 when 'C++','C'
129 when 'C++','C'
130 'ace/mode/c_cpp'
130 'ace/mode/c_cpp'
131 when 'Ruby'
131 when 'Ruby'
132 'ace/mode/ruby'
132 'ace/mode/ruby'
133 when 'Python'
133 when 'Python'
134 'ace/mode/python'
134 'ace/mode/python'
135 when 'Java'
135 when 'Java'
136 'ace/mode/java'
136 'ace/mode/java'
137 else
137 else
138 'ace/mode/c_cpp'
138 'ace/mode/c_cpp'
139 end
139 end
140 end
140 end
141
141
142
142
143 def user_title_bar(user)
143 def user_title_bar(user)
144 header = ''
144 header = ''
145 time_left = ''
145 time_left = ''
146
146
147 #
147 #
148 # if the contest is over
148 # if the contest is over
149 if GraderConfiguration.time_limit_mode?
149 if GraderConfiguration.time_limit_mode?
150 if user.contest_finished?
150 if user.contest_finished?
151 header = <<CONTEST_OVER
151 header = <<CONTEST_OVER
152 <tr><td colspan="2" align="center">
152 <tr><td colspan="2" align="center">
153 <span class="contest-over-msg">THE CONTEST IS OVER</span>
153 <span class="contest-over-msg">THE CONTEST IS OVER</span>
154 </td></tr>
154 </td></tr>
155 CONTEST_OVER
155 CONTEST_OVER
156 end
156 end
157 if !user.contest_started?
157 if !user.contest_started?
158 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
158 time_left = "&nbsp;&nbsp;" + (t 'title_bar.contest_not_started')
159 else
159 else
160 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
160 time_left = "&nbsp;&nbsp;" + (t 'title_bar.remaining_time') +
161 " #{format_short_duration(user.contest_time_left)}"
161 " #{format_short_duration(user.contest_time_left)}"
162 end
162 end
163 end
163 end
164
164
165 #
165 #
166 # if the contest is in the anaysis mode
166 # if the contest is in the anaysis mode
167 if GraderConfiguration.analysis_mode?
167 if GraderConfiguration.analysis_mode?
168 header = <<ANALYSISMODE
168 header = <<ANALYSISMODE
169 <tr><td colspan="2" align="center">
169 <tr><td colspan="2" align="center">
170 <span class="contest-over-msg">ANALYSIS MODE</span>
170 <span class="contest-over-msg">ANALYSIS MODE</span>
171 </td></tr>
171 </td></tr>
172 ANALYSISMODE
172 ANALYSISMODE
173 end
173 end
174
174
175 contest_name = GraderConfiguration['contest.name']
175 contest_name = GraderConfiguration['contest.name']
176
176
177 #
177 #
178 # build real title bar
178 # build real title bar
179 result = <<TITLEBAR
179 result = <<TITLEBAR
180 <div class="title">
180 <div class="title">
181 <table>
181 <table>
182 #{header}
182 #{header}
183 <tr>
183 <tr>
184 <td class="left-col">
184 <td class="left-col">
185 <br/>
185 <br/>
186 </td>
186 </td>
187 <td class="right-col">#{contest_name}</td>
187 <td class="right-col">#{contest_name}</td>
188 </tr>
188 </tr>
189 </table>
189 </table>
190 </div>
190 </div>
191 TITLEBAR
191 TITLEBAR
192 result.html_safe
192 result.html_safe
193 end
193 end
194
194
195 def markdown(text)
195 def markdown(text)
196 markdown = RDiscount.new(text)
196 markdown = RDiscount.new(text)
197 markdown.to_html.html_safe
197 markdown.to_html.html_safe
198 end
198 end
199
199
200
200
201 BOOTSTRAP_FLASH_MSG = {
201 BOOTSTRAP_FLASH_MSG = {
202 success: 'alert-success',
202 success: 'alert-success',
203 error: 'alert-danger',
203 error: 'alert-danger',
204 alert: 'alert-danger',
204 alert: 'alert-danger',
205 notice: 'alert-info'
205 notice: 'alert-info'
206 }
206 }
207
207
208 def bootstrap_class_for(flash_type)
208 def bootstrap_class_for(flash_type)
209 BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s)
209 BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s)
210 end
210 end
211
211
212 - def flash_messages
213 - flash.each do |msg_type, message|
214 - concat(content_tag(:div, message, class: "alert #{bootstrap_class_for(msg_type)} fade in") do
215 - concat content_tag(:button, 'x', class: "close", data: { dismiss: 'alert' })
216 - concat message
217 - end)
218 - end
219 - nil
220 - end
221 -
222 end
212 end
@@ -1,53 +1,56
1 // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
1 // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
2 //import "@hotwired/turbo-rails"
2 //import "@hotwired/turbo-rails"
3 //import "controllers"
3 //import "controllers"
4 //
4 //
5
5
6
6
7 //bootstrap
7 //bootstrap
8 //import "bootstrap"
8 //import "bootstrap"
9 //window.bootstrap = bootstrap
9 //window.bootstrap = bootstrap
10
10
11 //datatable
11 //datatable
12 //import 'datatables-bundle'
12 //import 'datatables-bundle'
13 import "pdfmake"
13 import "pdfmake"
14 import "pdfmake-vfs"
14 import "pdfmake-vfs"
15 import "jszip"
15 import "jszip"
16 import "datatables"
16 import "datatables"
17 import "datatables-bs5"
17 import "datatables-bs5"
18 import "datatables-editor"
18 import "datatables-editor"
19 import "datatables-editor-bs5"
19 import "datatables-editor-bs5"
20 import "datatables-autofill"
20 import "datatables-autofill"
21 import "datatables-autofill-bs5"
21 import "datatables-autofill-bs5"
22 import "datatables-button"
22 import "datatables-button"
23 import "datatables-button-bs5"
23 import "datatables-button-bs5"
24 import "datatables-button-colvis"
24 import "datatables-button-colvis"
25 import "datatables-button-html5"
25 import "datatables-button-html5"
26 import "datatables-button-print"
26 import "datatables-button-print"
27 import "datatables-colrecorder"
27 import "datatables-colrecorder"
28 import "datatables-datetime"
28 import "datatables-datetime"
29 import "datatables-fixedcolumns"
29 import "datatables-fixedcolumns"
30 import "datatables-fixedheader"
30 import "datatables-fixedheader"
31 import "datatables-keytable"
31 import "datatables-keytable"
32 import "datatables-responsive"
32 import "datatables-responsive"
33 import "datatables-responsive-bs5"
33 import "datatables-responsive-bs5"
34 import "datatables-rowgroup"
34 import "datatables-rowgroup"
35 import "datatables-rowreorder"
35 import "datatables-rowreorder"
36 import "datatables-scroller"
36 import "datatables-scroller"
37 import "datatables-searchbuilder"
37 import "datatables-searchbuilder"
38 import "datatables-searchbuilder-bs5"
38 import "datatables-searchbuilder-bs5"
39 import "datatables-searchpanes"
39 import "datatables-searchpanes"
40 import "datatables-searchpanes-bs5"
40 import "datatables-searchpanes-bs5"
41 import "datatables-select"
41 import "datatables-select"
42 import "datatables-staterestore"
42 import "datatables-staterestore"
43 import "datatables-staterestore-bs5"
43 import "datatables-staterestore-bs5"
44 /* */
44 /* */
45
45
46 import "select2"
46 import "select2"
47
47
48 //my own customization
48 //my own customization
49 import 'custom'
49 import 'custom'
50
50
51
51
52 + //trigger import map ready
52 console.log('application.js ready')
53 console.log('application.js ready')
53 -
54 + window.importmapScriptsLoaded = true
55 + const import_map_loaded = new CustomEvent('import-map-loaded', { });
56 + document.dispatchEvent(import_map_loaded);
@@ -1,34 +1,37
1 <!DOCTYPE html>
1 <!DOCTYPE html>
2 %html
2 %html
3 %head
3 %head
4 %title= GraderConfiguration['contest.name']
4 %title= GraderConfiguration['contest.name']
5 = stylesheet_link_tag "application", params[:controller], :media => "all"
5 = stylesheet_link_tag "application", params[:controller], :media => "all"
6 = csrf_meta_tags
6 = csrf_meta_tags
7 = csp_meta_tag
7 = csp_meta_tag
8 = javascript_include_tag :my_app
8 = javascript_include_tag :my_app
9 -# = javascript_import_module_tag('prepend_jquery')
9 -# = javascript_import_module_tag('prepend_jquery')
10 = javascript_importmap_tags
10 = javascript_importmap_tags
11 = content_for :header
11 = content_for :header
12 = yield :head
12 = yield :head
13 -# %link{href:"https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css",rel:"stylesheet",integrity:"sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT",crossorigin:"anonymous"}
13 -# %link{href:"https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css",rel:"stylesheet",integrity:"sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT",crossorigin:"anonymous"}
14 -# %script{src:"https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js",integrity:"sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3",crossorigin:"anonymous"}
14 -# %script{src:"https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js",integrity:"sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3",crossorigin:"anonymous"}
15 -# %script{src:"https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js",integrity:"sha384-7VPbUDkoPSGFnVtYi0QogXtr74QeVeeIs99Qfg5YCF+TidwNdjvaKZX19NZ/e6oz",crossorigin:"anonymous"}
15 -# %script{src:"https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js",integrity:"sha384-7VPbUDkoPSGFnVtYi0QogXtr74QeVeeIs99Qfg5YCF+TidwNdjvaKZX19NZ/e6oz",crossorigin:"anonymous"}
16
16
17 <link rel="preconnect" href="https://fonts.googleapis.com">
17 <link rel="preconnect" href="https://fonts.googleapis.com">
18 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
18 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
19 -#
19 -#
20 <link href="https://fonts.googleapis.com/css2?family=Bai+Jamjuree:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&family=Krub:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&family=Sarabun:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&display=swap" rel="stylesheet">
20 <link href="https://fonts.googleapis.com/css2?family=Bai+Jamjuree:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&family=Krub:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&family=Sarabun:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&display=swap" rel="stylesheet">
21 <link href="https://fonts.googleapis.com/css2?family=Mitr:ital,wght@0,300;1,300&family=Kodchasan:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&family=Noto+Serif+Thai:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&family=Noto+Sans+Thai:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&display=swap" rel="stylesheet">
21 <link href="https://fonts.googleapis.com/css2?family=Mitr:ital,wght@0,300;1,300&family=Kodchasan:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&family=Noto+Serif+Thai:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&family=Noto+Sans+Thai:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&display=swap" rel="stylesheet">
22 <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+Thai:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&family=Noto+Sans+Thai:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&display=swap" rel="stylesheet">
22 <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+Thai:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&family=Noto+Sans+Thai:ital,wght@0,200;0,400;0,600;1,200;1,400;1,600&display=swap" rel="stylesheet">
23 <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+Thai:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap" rel="stylesheet">
23 <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+Thai:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap" rel="stylesheet">
24 <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Thai:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap" rel="stylesheet">
24 <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Thai:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap" rel="stylesheet">
25 <link href="https://fonts.googleapis.com/css2?family=Sarabun:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap" rel="stylesheet">
25 <link href="https://fonts.googleapis.com/css2?family=Sarabun:ital,wght@0,300;0,400;0,500;1,300;1,400;1,500&display=swap" rel="stylesheet">
26
26
27 %body
27 %body
28 - unless local_assigns[:skip_header]
28 - unless local_assigns[:skip_header]
29 = render 'layouts/header'
29 = render 'layouts/header'
30
30
31 /= content_tag(:p,flash[:notice],class: 'alert alert-success') if flash[:notice]!=nil
31 /= content_tag(:p,flash[:notice],class: 'alert alert-success') if flash[:notice]!=nil
32 .container-fluid
32 .container-fluid
33 - = flash_messages
33 + - flash.each do |msg_type, message|
34 + .alert.alert-dismissible.fade.show{class: bootstrap_class_for(msg_type)}
35 + = message
36 + %button.btn-close{type: 'button', 'data-bs-dismiss': :alert}
34 = yield
37 = yield
@@ -1,12 +1,20
1 = simple_form_for(@user) do |f|
1 = simple_form_for(@user) do |f|
2 = f.error_notification
2 = f.error_notification
3 - = f.input :login, label: 'Login'
3 + .mb-2
4 - = f.input :full_name, label: 'Full name'
4 + = f.input :login, label: 'Login'
5 - = f.input :password
5 + .mb-2
6 - = f.input :password_confirmation
6 + = f.input :full_name, label: 'Full name'
7 - = f.input :email
7 + .mb-2
8 - = f.input :alias
8 + = f.input :password
9 - = f.input :remark
9 + .mb-2
10 - = f.button :submit, class: 'btn btn-primary'
10 + = f.input :password_confirmation
11 - = link_to 'Cancel', :back, class: 'btn btn-default'
11 + .mb-2
12 + = f.input :email
13 + .mb-2
14 + = f.input :alias
15 + .mb-2
16 + = f.input :remark
17 + .mb-2
18 + = f.button :submit, class: 'btn btn-primary'
19 + = link_to 'Cancel', :back, class: 'btn btn-secondary'
12
20
@@ -1,106 +1,101
1 %h1 Users
1 %h1 Users
2
2
3 - .card.border-primary
3 + .card.border-success.mb-3
4 - .card-header.text-bg-primary.border-primary
4 + .card-header.text-bg-success.border-success
5 Quick Add
5 Quick Add
6 .card-body
6 .card-body
7 - = form_with url: 'asd', class: 'row row-cols-lg-auto g-3 align-items-center' do |f|
7 + = form_with url: user_admin_index_path, scope: :user, class: 'row row-cols-lg-auto g-3 align-items-center' do |f|
8 .col-12
8 .col-12
9 - = f.label 'user_login', 'Login'
9 + = f.text_field 'login', :size => 10,class: 'form-control', placeholder: 'login'
10 - = f.text_field 'login', :size => 10,class: 'form-control'
11 .form-group
10 .form-group
12 - = f.label 'user_full_name', 'Full Name'
11 + = f.text_field 'full_name', :size => 10,class: 'form-control', placeholder: 'full name'
13 - = f.text_field 'full_name', :size => 10,class: 'form-control'
14 .form-group
12 .form-group
15 - = f.label 'user_password', 'Password'
13 + = f.password_field 'password', :size => 10,class: 'form-control', placeholder: 'password'
16 - = f.text_field 'password', :size => 10,class: 'form-control'
17 .form-group
14 .form-group
18 - = f.label 'user_password_confirmation', 'Confirm'
15 + = f.password_field 'password_confirmation', :size => 10,class: 'form-control', placeholder: 'password confirmation'
19 - = f.text_field 'password_confirmation', :size => 10,class: 'form-control'
20 .form-group
16 .form-group
21 - = f.label 'user_email', 'email'
17 + = f.text_field 'email', :size => 10,class: 'form-control', placeholder: 'email'
22 - = f.text_field 'email', :size => 10,class: 'form-control'
18 + =submit_tag "Create", class: 'btn btn-success align-items-bottom'
23 - =submit_tag "Create", class: 'btn btn-primary align-items-bottom'
24
19
25 - .panel.panel-primary
20 + .card.border-success.mb-3
26 - .panel-title.panel-heading
21 + .card-header.text-bg-success.border-success
27 Import from site management
22 Import from site management
28 - .panel-body
23 + .card-body
29 - = form_tag({:action => 'import'}, :multipart => true,class: 'form form-inline') do
24 + = form_with url: import_user_admin_index_path, :multipart => true do |f|
30 - .form-group
25 + .row
31 - = label_tag :file, 'File:'
26 + .col-auto
32 - .input-group
27 + = f.label :file, 'File:', class: 'col-form-label'
33 - %span.input-group-btn
28 + .col-auto
34 - %span.btn.btn-default.btn-file
29 + = f.file_field :file, class: 'form-control'
35 - Browse
30 + .col-auto
36 - = file_field_tag 'file'
31 + = f.submit 'Submit', class: 'btn btn-secondary'
37 - = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
38 - = submit_tag 'Submit', class: 'btn btn-default'
39
32
40
33
41 %p
34 %p
42 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
35 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
43 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
36 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
44 - = link_to 'Bulk Manage', { action: :bulk_manage} , { class: 'btn btn-default btn-info'}
37 + = link_to 'Bulk Manage', { action: :bulk_manage} , { class: 'btn btn-secondary btn-info'}
45 - = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn-default '}
38 + = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn-secondary '}
46 - = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn-default '}
39 + = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn-secondary '}
47 - = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn-default '}
40 + = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn-secondary '}
48 - = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn-default '}
41 + = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn-secondary '}
49
42
50 - if GraderConfiguration.multicontests?
43 - if GraderConfiguration.multicontests?
51 %br/
44 %br/
52 %b Multi-contest:
45 %b Multi-contest:
53 = link_to '[Manage bulk users in contests]', :action => 'contest_management'
46 = link_to '[Manage bulk users in contests]', :action => 'contest_management'
54 View users in:
47 View users in:
55 - @contests.each do |contest|
48 - @contests.each do |contest|
56 = link_to "[#{contest.name}]", :action => 'contests', :id => contest.id
49 = link_to "[#{contest.name}]", :action => 'contests', :id => contest.id
57 = link_to "[no contest]", :action => 'contests', :id => 'none'
50 = link_to "[no contest]", :action => 'contests', :id => 'none'
58
51
59 -# Total #{@user_count} users |
52 -# Total #{@user_count} users |
60 -# - if !@paginated
53 -# - if !@paginated
61 -# Display all users.
54 -# Display all users.
62 -# \#{link_to '[show in pages]', :action => 'index', :page => '1'}
55 -# \#{link_to '[show in pages]', :action => 'index', :page => '1'}
63 -# - else
56 -# - else
64 -# Display in pages.
57 -# Display in pages.
65 -# \#{link_to '[display all]', :action => 'index', :page => 'all'} |
58 -# \#{link_to '[display all]', :action => 'index', :page => 'all'} |
66 -# \#{will_paginate @users, :container => false}
59 -# \#{will_paginate @users, :container => false}
67
60
68
61
69 %table.table.table-hover.table-condense.datatable
62 %table.table.table-hover.table-condense.datatable
70 %thead
63 %thead
71 %th Login
64 %th Login
72 %th Full name
65 %th Full name
73 %th email
66 %th email
74 %th Remark
67 %th Remark
75 %th
68 %th
76 Activated
69 Activated
77 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'User has already confirmed the email?' } [?]
70 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'User has already confirmed the email?' } [?]
78 %th
71 %th
79 Enabled
72 Enabled
80 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'Allow the user to login?' } [?]
73 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'Allow the user to login?' } [?]
81 %th Last IP
74 %th Last IP
82 %th
75 %th
83 %th
76 %th
84 %th
77 %th
85 %th
78 %th
86 - for user in @users
79 - for user in @users
87 %tr
80 %tr
88 %td= link_to user.login, stat_user_path(user)
81 %td= link_to user.login, stat_user_path(user)
89 %td= user.full_name
82 %td= user.full_name
90 %td= user.email
83 %td= user.email
91 %td= user.remark
84 %td= user.remark
92 %td= toggle_button(user.activated?, toggle_activate_user_path(user),"toggle_activate_user_#{user.id}")
85 %td= toggle_button(user.activated?, toggle_activate_user_path(user),"toggle_activate_user_#{user.id}")
93 %td= toggle_button(user.enabled?, toggle_enable_user_path(user),"toggle_enable_user_#{user.id}")
86 %td= toggle_button(user.enabled?, toggle_enable_user_path(user),"toggle_enable_user_#{user.id}")
94 %td= user.last_ip
87 %td= user.last_ip
95 %td= link_to 'Clear IP', {:action => 'clear_last_ip', :id => user, :page=>params[:page]}, :confirm => 'This will reset last logging in ip of the user, are you sure?', class: 'btn btn-default btn-xs btn-block'
88 %td= link_to 'Clear IP', {:action => 'clear_last_ip', :id => user, :page=>params[:page]}, :confirm => 'This will reset last logging in ip of the user, are you sure?', class: 'btn btn-default btn-xs btn-block'
96 %td= link_to 'Show', {:action => 'show', :id => user}, class: 'btn btn-default btn-xs btn-block'
89 %td= link_to 'Show', {:action => 'show', :id => user}, class: 'btn btn-default btn-xs btn-block'
97 %td= link_to 'Edit', {:action => 'edit', :id => user}, class: 'btn btn-default btn-xs btn-block'
90 %td= link_to 'Edit', {:action => 'edit', :id => user}, class: 'btn btn-default btn-xs btn-block'
98 %td= link_to 'Destroy', {action: :destroy, id: user}, data: {confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-danger btn-xs btn-block'
91 %td= link_to 'Destroy', {action: :destroy, id: user}, data: {confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-danger btn-xs btn-block'
99 %br/
92 %br/
100 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
93 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
101 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
94 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
102
95
103 :javascript
96 :javascript
104 - $('.datatable').DataTable({
97 + $(document).on('import-map-loaded',(e) => {
105 - 'pageLength': 50
98 + $('.datatable').DataTable({
106 - });
99 + 'pageLength': 50
100 + });
101 + })
@@ -1,54 +1,56
1 .container-fluid
1 .container-fluid
2 .row
2 .row
3 .col-md-6
3 .col-md-6
4 %h1 Adding list of users
4 %h1 Adding list of users
5 - .row
5 + .row.my-3
6 .col-md-6
6 .col-md-6
7 - .panel.panel-default
7 + = form_with url: create_from_list_user_admin_index_path do |f|
8 - .panel-heading
8 + .row.align-items-center.mb-3
9 - .panel-title Info
9 + .col-auto
10 - .panel-body
10 + = f.submit 'Create following users',class: 'btn btn-success'
11 + .col-auto
12 + .form-check
13 + = f.check_box :add_to_group, class: 'form-check-input'
14 + = f.label :add_to_group, 'Also add these users to the following group', class: 'form-check-label'
15 + .col-4
16 + = f.select "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), {}, class: 'select2 form-control'
17 + .row.mb-3
18 + .col-12
19 + = f.text_area :user_list, value: nil, class: 'form-control', style: 'height: 30rem'
20 + .col-md-6
21 + .card.card-default
22 + .card-header
23 + .card-title Info
24 + .card-body
11 %ul
25 %ul
12 %li
26 %li
13 List of user information in this format:
27 List of user information in this format:
14 %tt user_id,name(,passwd(,alias(,remark)))
28 %tt user_id,name(,passwd(,alias(,remark)))
15 %li
29 %li
16 Note that
30 Note that
17 %tt passwd, alias
31 %tt passwd, alias
18 and
32 and
19 %tt remark
33 %tt remark
20 is optional.
34 is optional.
21 %li
35 %li
22 When
36 When
23 %tt passwd
37 %tt passwd
24 or
38 or
25 %tt alias
39 %tt alias
26 is empty, the original value will be used instead.
40 is empty, the original value will be used instead.
27 %li
41 %li
28 If the users with the same user_id already exists, existing information will be overwritten.
42 If the users with the same user_id already exists, existing information will be overwritten.
29 Example:
43 Example:
30 %ol
44 %ol
31 %li
45 %li
32 %pre user1,Somchai Jaidee
46 %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.
47 will create (or update) a user with login "user1" and setting the fullname to "Somchai Jaidee", also setting a random password.
34 %li
48 %li
35 %pre user1,Somchai Jaidee,
49 %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.
50 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
51
52 + :javascript
53 + $(document).on('import-map-loaded',(e) => {
54 + $('.select2').select2()
55 + });
38
56
39 - .row
40 - .col-md-6
41 - = form_tag :action => 'create_from_list' do
42 - .form-group
43 - = submit_tag 'Create following users',class: 'btn btn-success'
44 - .form-group
45 - .div.checkbox
46 - %label
47 - = check_box_tag :add_to_group
48 - Also add these users to the following group
49 - = select_tag "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'select2'
50 - .form-group
51 - = text_area_tag 'user_list', nil, :rows => 50, :cols => 80
52 - .col-md-6
53 -
54 -
@@ -1,38 +1,38
1 # Be sure to restart your server when you modify this file.
1 # Be sure to restart your server when you modify this file.
2 #
2 #
3 # This file contains migration options to ease your Rails 5.2 upgrade.
3 # This file contains migration options to ease your Rails 5.2 upgrade.
4 #
4 #
5 # Once upgraded flip defaults one by one to migrate to the new default.
5 # Once upgraded flip defaults one by one to migrate to the new default.
6 #
6 #
7 # Read the Guide for Upgrading Ruby on Rails for more info on each option.
7 # Read the Guide for Upgrading Ruby on Rails for more info on each option.
8
8
9 # Make Active Record use stable #cache_key alongside new #cache_version method.
9 # Make Active Record use stable #cache_key alongside new #cache_version method.
10 # This is needed for recyclable cache keys.
10 # This is needed for recyclable cache keys.
11 # Rails.application.config.active_record.cache_versioning = true
11 # Rails.application.config.active_record.cache_versioning = true
12
12
13 # Use AES-256-GCM authenticated encryption for encrypted cookies.
13 # Use AES-256-GCM authenticated encryption for encrypted cookies.
14 # Also, embed cookie expiry in signed or encrypted cookies for increased security.
14 # Also, embed cookie expiry in signed or encrypted cookies for increased security.
15 #
15 #
16 # This option is not backwards compatible with earlier Rails versions.
16 # This option is not backwards compatible with earlier Rails versions.
17 # It's best enabled when your entire app is migrated and stable on 5.2.
17 # It's best enabled when your entire app is migrated and stable on 5.2.
18 #
18 #
19 # Existing cookies will be converted on read then written with the new scheme.
19 # Existing cookies will be converted on read then written with the new scheme.
20 # Rails.application.config.action_dispatch.use_authenticated_cookie_encryption = true
20 # Rails.application.config.action_dispatch.use_authenticated_cookie_encryption = true
21
21
22 # Use AES-256-GCM authenticated encryption as default cipher for encrypting messages
22 # Use AES-256-GCM authenticated encryption as default cipher for encrypting messages
23 # instead of AES-256-CBC, when use_authenticated_message_encryption is set to true.
23 # instead of AES-256-CBC, when use_authenticated_message_encryption is set to true.
24 # Rails.application.config.active_support.use_authenticated_message_encryption = true
24 # Rails.application.config.active_support.use_authenticated_message_encryption = true
25
25
26 # Add default protection from forgery to ActionController::Base instead of in
26 # Add default protection from forgery to ActionController::Base instead of in
27 # ApplicationController.
27 # ApplicationController.
28 # Rails.application.config.action_controller.default_protect_from_forgery = true
28 # Rails.application.config.action_controller.default_protect_from_forgery = true
29
29
30 # Store boolean values are in sqlite3 databases as 1 and 0 instead of 't' and
30 # Store boolean values are in sqlite3 databases as 1 and 0 instead of 't' and
31 # 'f' after migrating old data.
31 # 'f' after migrating old data.
32 # Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true
32 # Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true
33
33
34 # Use SHA-1 instead of MD5 to generate non-sensitive digests, such as the ETag header.
34 # Use SHA-1 instead of MD5 to generate non-sensitive digests, such as the ETag header.
35 # Rails.application.config.active_support.use_sha1_digests = true
35 # Rails.application.config.active_support.use_sha1_digests = true
36
36
37 # Make `form_with` generate id attributes for any generated HTML tags.
37 # Make `form_with` generate id attributes for any generated HTML tags.
38 - # Rails.application.config.action_view.form_with_generates_ids = true
38 + Rails.application.config.action_view.form_with_generates_ids = true
You need to be logged in to leave comments. Login now