Description:
user admin
Commit status:
[Not Reviewed]
References:
Diff options:
Comments:
0 Commit comments
0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
r875:567f1ebe063b - - 8 files changed: 80 inserted, 78 deleted
@@ -2,24 +2,25 | |||
|
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 |
@@ -200,23 +200,13 | |||
|
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 |
@@ -40,14 +40,17 | |||
|
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); |
@@ -21,14 +21,17 | |||
|
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 |
- |
|
|
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 : |
|
|
5 | - = f.input :password | |
|
6 | - = f.input :password_confirmation | |
|
7 | - = f.input :email | |
|
8 |
- = f.input : |
|
|
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,60 +1,53 | |||
|
1 | 1 | %h1 Users |
|
2 | 2 | |
|
3 |
- .card.border- |
|
|
4 |
- .card-header.text-bg- |
|
|
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: |
|
|
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 |
- . |
|
|
29 | - = form_tag({:action => 'import'}, :multipart => true,class: 'form form-inline') do | |
|
30 |
- . |
|
|
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- |
|
|
45 |
- = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn- |
|
|
46 |
- = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn- |
|
|
47 |
- = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn- |
|
|
48 |
- = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn- |
|
|
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 |
@@ -92,15 +85,17 | |||
|
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,22 +1,36 | |||
|
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 |
- . |
|
|
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 |
@@ -26,29 +40,17 | |||
|
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 | - |
@@ -26,13 +26,13 | |||
|
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 |
- |
|
|
38 | + Rails.application.config.action_view.form_with_generates_ids = true |
You need to be logged in to leave comments.
Login now