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,37 +1,38
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 + @user = User.new
14 15 end
15 16
16 17 def active
17 18 sessions = ActiveRecord::SessionStore::Session.where("updated_at >= ?", 60.minutes.ago)
18 19 @users = []
19 20 sessions.each do |session|
20 21 if session.data[:user_id]
21 22 @users << User.find(session.data[:user_id])
22 23 end
23 24 end
24 25 end
25 26
26 27 def show
27 28 @user = User.find(params[:id])
28 29 end
29 30
30 31 def new
31 32 @user = User.new
32 33 end
33 34
34 35 def create
35 36 @user = User.new(user_params)
36 37 @user.activated = true
37 38 if @user.save
@@ -188,35 +188,25
188 188 </tr>
189 189 </table>
190 190 </div>
191 191 TITLEBAR
192 192 result.html_safe
193 193 end
194 194
195 195 def markdown(text)
196 196 markdown = RDiscount.new(text)
197 197 markdown.to_html.html_safe
198 198 end
199 199
200 200
201 201 BOOTSTRAP_FLASH_MSG = {
202 202 success: 'alert-success',
203 203 error: 'alert-danger',
204 204 alert: 'alert-danger',
205 205 notice: 'alert-info'
206 206 }
207 207
208 208 def bootstrap_class_for(flash_type)
209 209 BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s)
210 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 212 end
@@ -28,26 +28,29
28 28 import "datatables-datetime"
29 29 import "datatables-fixedcolumns"
30 30 import "datatables-fixedheader"
31 31 import "datatables-keytable"
32 32 import "datatables-responsive"
33 33 import "datatables-responsive-bs5"
34 34 import "datatables-rowgroup"
35 35 import "datatables-rowreorder"
36 36 import "datatables-scroller"
37 37 import "datatables-searchbuilder"
38 38 import "datatables-searchbuilder-bs5"
39 39 import "datatables-searchpanes"
40 40 import "datatables-searchpanes-bs5"
41 41 import "datatables-select"
42 42 import "datatables-staterestore"
43 43 import "datatables-staterestore-bs5"
44 44 /* */
45 45
46 46 import "select2"
47 47
48 48 //my own customization
49 49 import 'custom'
50 50
51 51
52 + //trigger import map ready
52 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);
@@ -9,26 +9,29
9 9 -# = javascript_import_module_tag('prepend_jquery')
10 10 = javascript_importmap_tags
11 11 = content_for :header
12 12 = yield :head
13 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 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 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 17 <link rel="preconnect" href="https://fonts.googleapis.com">
18 18 <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
19 19 -#
20 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 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 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 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 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 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 27 %body
28 28 - unless local_assigns[:skip_header]
29 29 = render 'layouts/header'
30 30
31 31 /= content_tag(:p,flash[:notice],class: 'alert alert-success') if flash[:notice]!=nil
32 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 37 = yield
@@ -1,12 +1,20
1 1 = simple_form_for(@user) do |f|
2 2 = f.error_notification
3 - = f.input :login, label: 'Login'
4 - = f.input :full_name, label: 'Full name'
5 - = f.input :password
6 - = f.input :password_confirmation
7 - = f.input :email
8 - = f.input :alias
9 - = f.input :remark
10 - = f.button :submit, class: 'btn btn-primary'
11 - = link_to 'Cancel', :back, class: 'btn btn-default'
3 + .mb-2
4 + = f.input :login, label: 'Login'
5 + .mb-2
6 + = f.input :full_name, label: 'Full name'
7 + .mb-2
8 + = f.input :password
9 + .mb-2
10 + = f.input :password_confirmation
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,72 +1,65
1 1 %h1 Users
2 2
3 - .card.border-primary
4 - .card-header.text-bg-primary.border-primary
3 + .card.border-success.mb-3
4 + .card-header.text-bg-success.border-success
5 5 Quick Add
6 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 8 .col-12
9 - = f.label 'user_login', 'Login'
10 - = f.text_field 'login', :size => 10,class: 'form-control'
9 + = f.text_field 'login', :size => 10,class: 'form-control', placeholder: 'login'
11 10 .form-group
12 - = f.label 'user_full_name', 'Full Name'
13 - = f.text_field 'full_name', :size => 10,class: 'form-control'
11 + = f.text_field 'full_name', :size => 10,class: 'form-control', placeholder: 'full name'
14 12 .form-group
15 - = f.label 'user_password', 'Password'
16 - = f.text_field 'password', :size => 10,class: 'form-control'
13 + = f.password_field 'password', :size => 10,class: 'form-control', placeholder: 'password'
17 14 .form-group
18 - = f.label 'user_password_confirmation', 'Confirm'
19 - = f.text_field 'password_confirmation', :size => 10,class: 'form-control'
15 + = f.password_field 'password_confirmation', :size => 10,class: 'form-control', placeholder: 'password confirmation'
20 16 .form-group
21 - = f.label 'user_email', 'email'
22 - = f.text_field 'email', :size => 10,class: 'form-control'
23 - =submit_tag "Create", class: 'btn btn-primary align-items-bottom'
17 + = f.text_field 'email', :size => 10,class: 'form-control', placeholder: 'email'
18 + =submit_tag "Create", class: 'btn btn-success align-items-bottom'
24 19
25 - .panel.panel-primary
26 - .panel-title.panel-heading
20 + .card.border-success.mb-3
21 + .card-header.text-bg-success.border-success
27 22 Import from site management
28 - .panel-body
29 - = form_tag({:action => 'import'}, :multipart => true,class: 'form form-inline') do
30 - .form-group
31 - = label_tag :file, 'File:'
32 - .input-group
33 - %span.input-group-btn
34 - %span.btn.btn-default.btn-file
35 - Browse
36 - = file_field_tag 'file'
37 - = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
38 - = submit_tag 'Submit', class: 'btn btn-default'
23 + .card-body
24 + = form_with url: import_user_admin_index_path, :multipart => true do |f|
25 + .row
26 + .col-auto
27 + = f.label :file, 'File:', class: 'col-form-label'
28 + .col-auto
29 + = f.file_field :file, class: 'form-control'
30 + .col-auto
31 + = f.submit 'Submit', class: 'btn btn-secondary'
39 32
40 33
41 34 %p
42 35 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
43 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'}
45 - = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn-default '}
46 - = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn-default '}
47 - = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn-default '}
48 - = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn-default '}
37 + = link_to 'Bulk Manage', { action: :bulk_manage} , { class: 'btn btn-secondary btn-info'}
38 + = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn-secondary '}
39 + = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn-secondary '}
40 + = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn-secondary '}
41 + = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn-secondary '}
49 42
50 43 - if GraderConfiguration.multicontests?
51 44 %br/
52 45 %b Multi-contest:
53 46 = link_to '[Manage bulk users in contests]', :action => 'contest_management'
54 47 View users in:
55 48 - @contests.each do |contest|
56 49 = link_to "[#{contest.name}]", :action => 'contests', :id => contest.id
57 50 = link_to "[no contest]", :action => 'contests', :id => 'none'
58 51
59 52 -# Total #{@user_count} users |
60 53 -# - if !@paginated
61 54 -# Display all users.
62 55 -# \#{link_to '[show in pages]', :action => 'index', :page => '1'}
63 56 -# - else
64 57 -# Display in pages.
65 58 -# \#{link_to '[display all]', :action => 'index', :page => 'all'} |
66 59 -# \#{will_paginate @users, :container => false}
67 60
68 61
69 62 %table.table.table-hover.table-condense.datatable
70 63 %thead
71 64 %th Login
72 65 %th Full name
@@ -80,27 +73,29
80 73 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'Allow the user to login?' } [?]
81 74 %th Last IP
82 75 %th
83 76 %th
84 77 %th
85 78 %th
86 79 - for user in @users
87 80 %tr
88 81 %td= link_to user.login, stat_user_path(user)
89 82 %td= user.full_name
90 83 %td= user.email
91 84 %td= user.remark
92 85 %td= toggle_button(user.activated?, toggle_activate_user_path(user),"toggle_activate_user_#{user.id}")
93 86 %td= toggle_button(user.enabled?, toggle_enable_user_path(user),"toggle_enable_user_#{user.id}")
94 87 %td= user.last_ip
95 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 89 %td= link_to 'Show', {:action => 'show', :id => user}, class: 'btn btn-default btn-xs btn-block'
97 90 %td= link_to 'Edit', {:action => 'edit', :id => user}, class: 'btn btn-default btn-xs btn-block'
98 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 92 %br/
100 93 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
101 94 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
102 95
103 96 :javascript
104 - $('.datatable').DataTable({
105 - 'pageLength': 50
106 - });
97 + $(document).on('import-map-loaded',(e) => {
98 + $('.datatable').DataTable({
99 + 'pageLength': 50
100 + });
101 + })
@@ -1,54 +1,56
1 1 .container-fluid
2 2 .row
3 3 .col-md-6
4 4 %h1 Adding list of users
5 - .row
5 + .row.my-3
6 6 .col-md-6
7 - .panel.panel-default
8 - .panel-heading
9 - .panel-title Info
10 - .panel-body
7 + = form_with url: create_from_list_user_admin_index_path do |f|
8 + .row.align-items-center.mb-3
9 + .col-auto
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 25 %ul
12 26 %li
13 27 List of user information in this format:
14 28 %tt user_id,name(,passwd(,alias(,remark)))
15 29 %li
16 30 Note that
17 31 %tt passwd, alias
18 32 and
19 33 %tt remark
20 34 is optional.
21 35 %li
22 36 When
23 37 %tt passwd
24 38 or
25 39 %tt alias
26 40 is empty, the original value will be used instead.
27 41 %li
28 42 If the users with the same user_id already exists, existing information will be overwritten.
29 43 Example:
30 44 %ol
31 45 %li
32 46 %pre user1,Somchai Jaidee
33 47 will create (or update) a user with login "user1" and setting the fullname to "Somchai Jaidee", also setting a random password.
34 48 %li
35 49 %pre user1,Somchai Jaidee,
36 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 -
@@ -14,25 +14,25
14 14 # Also, embed cookie expiry in signed or encrypted cookies for increased security.
15 15 #
16 16 # This option is not backwards compatible with earlier Rails versions.
17 17 # It's best enabled when your entire app is migrated and stable on 5.2.
18 18 #
19 19 # Existing cookies will be converted on read then written with the new scheme.
20 20 # Rails.application.config.action_dispatch.use_authenticated_cookie_encryption = true
21 21
22 22 # Use AES-256-GCM authenticated encryption as default cipher for encrypting messages
23 23 # instead of AES-256-CBC, when use_authenticated_message_encryption is set to true.
24 24 # Rails.application.config.active_support.use_authenticated_message_encryption = true
25 25
26 26 # Add default protection from forgery to ActionController::Base instead of in
27 27 # ApplicationController.
28 28 # Rails.application.config.action_controller.default_protect_from_forgery = true
29 29
30 30 # Store boolean values are in sqlite3 databases as 1 and 0 instead of 't' and
31 31 # 'f' after migrating old data.
32 32 # Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true
33 33
34 34 # Use SHA-1 instead of MD5 to generate non-sensitive digests, such as the ETag header.
35 35 # Rails.application.config.active_support.use_sha1_digests = true
36 36
37 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