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

r876:8cd1c60e1ef0 - - The requested commit is too big and content was truncated. 22 files changed. Show full diff

@@ -0,0 +1,6
1 + %h1 New Problem
2 +
3 + = render 'form', problem: @problem
4 + .row.my-3
5 + .col-md-4
6 + = link_to 'Back', problems_path, class: 'btn btn-secondary'
@@ -0,0 +1,57
1 + # This migration comes from active_storage (originally 20170806125915)
2 + class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
3 + def change
4 + # Use Active Record's configured type for primary and foreign keys
5 + primary_key_type, foreign_key_type = primary_and_foreign_key_types
6 +
7 + create_table :active_storage_blobs, id: primary_key_type do |t|
8 + t.string :key, null: false
9 + t.string :filename, null: false
10 + t.string :content_type
11 + t.text :metadata
12 + t.string :service_name, null: false
13 + t.bigint :byte_size, null: false
14 + t.string :checksum
15 +
16 + if connection.supports_datetime_with_precision?
17 + t.datetime :created_at, precision: 6, null: false
18 + else
19 + t.datetime :created_at, null: false
20 + end
21 +
22 + t.index [ :key ], unique: true
23 + end
24 +
25 + create_table :active_storage_attachments, id: primary_key_type do |t|
26 + t.string :name, null: false
27 + t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type
28 + t.references :blob, null: false, type: foreign_key_type
29 +
30 + if connection.supports_datetime_with_precision?
31 + t.datetime :created_at, precision: 6, null: false
32 + else
33 + t.datetime :created_at, null: false
34 + end
35 +
36 + t.index [ :record_type, :record_id, :name, :blob_id ], name: :index_active_storage_attachments_uniqueness, unique: true
37 + t.foreign_key :active_storage_blobs, column: :blob_id
38 + end
39 +
40 + create_table :active_storage_variant_records, id: primary_key_type do |t|
41 + t.belongs_to :blob, null: false, index: false, type: foreign_key_type
42 + t.string :variation_digest, null: false
43 +
44 + t.index [ :blob_id, :variation_digest ], name: :index_active_storage_variant_records_uniqueness, unique: true
45 + t.foreign_key :active_storage_blobs, column: :blob_id
46 + end
47 + end
48 +
49 + private
50 + def primary_and_foreign_key_types
51 + config = Rails.configuration.generators
52 + setting = config.options[config.orm][:primary_key_type]
53 + primary_key_type = setting || :primary_key
54 + foreign_key_type = setting || :bigint
55 + [primary_key_type, foreign_key_type]
56 + end
57 + end
@@ -0,0 +1,6
1 + class AddDescriptionToProblems < ActiveRecord::Migration[7.0]
2 + def change
3 + add_column :problems, :description, :text
4 + add_column :problems, :markdown, :boolean
5 + end
6 + end
@@ -0,0 +1,21
1 + Problem.all.each do |p|
2 + next unless p.description_filename
3 + basename, ext = p.description_filename.split('.')
4 + filename = "#{Problem.download_file_basedir}/#{p.id}/#{basename}.#{ext}"
5 +
6 + if File.exists? filename
7 + p.statement.attach io: File.open(filename), filename: "#{basename}.#{ext}"
8 + puts "#{p.id}: OK"
9 + else
10 + puts "#{p.id}: #{p.name} #{filename} ERROR"
11 + end
12 +
13 + d = Description.where(id: p.description_id).first
14 + if d
15 + p.description = d.body
16 + p.markdown = d.markdowned
17 + end
18 + p.save
19 +
20 +
21 + end
@@ -35,3 +35,4
35 35 /config/secrets.yml
36 36
37 37 /.byebug_history
38 + /storage/*
@@ -77,7 +77,8
77 77 #gem 'jquery-datatables-rails'
78 78
79 79 #----------- user interface -----------------
80 - gem 'simple_form'
80 + gem 'simple_form', git: 'https://github.com/heartcombo/simple_form', ref: '31fe255'
81 +
81 82 #select 2
82 83 #gem 'select2-rails'
83 84 #ace editor
@@ -1,3 +1,12
1 + GIT
2 + remote: https://github.com/heartcombo/simple_form
3 + revision: 31fe25504771bd6cd425b585a4e0ed652fba4521
4 + ref: 31fe255
5 + specs:
6 + simple_form (5.1.0)
7 + actionpack (>= 5.2)
8 + activemodel (>= 5.2)
9 +
1 10 GIT
2 11 remote: https://github.com/mmotherwell/best_in_place
3 12 revision: 88eb3052623a9a6cd346864d2aca05021c2f80d0
@@ -247,9 +256,6
247 256 rubyzip (>= 1.2.2, < 3.0)
248 257 websocket (~> 1.0)
249 258 sexp_processor (4.16.1)
250 - simple_form (5.1.0)
251 - actionpack (>= 5.2)
252 - activemodel (>= 5.2)
253 259 spring (2.1.1)
254 260 spring-watcher-listen (2.0.1)
255 261 listen (>= 2.7, < 4.0)
@@ -315,7 +321,7
315 321 rouge
316 322 sassc-rails
317 323 selenium-webdriver
318 - simple_form
324 + simple_form!
319 325 spring
320 326 spring-watcher-listen (~> 2.0.0)
321 327 sprockets-rails
@@ -59,10 +59,6
59 59 }
60 60 }
61 61
62 - input {
63 - font-family: Tahoma, "sans-serif";
64 - }
65 -
66 62 h1 {
67 63 color: #334488;
68 64 }
@@ -7,6 +7,7
7 7 before_action :current_user
8 8 before_action :nav_announcement
9 9 before_action :unique_visitor_id
10 + before_action :active_controller_action
10 11
11 12 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
12 13 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
@@ -29,6 +30,12
29 30 @nav_announcement = Announcement.where(on_nav_bar: true)
30 31 end
31 32
33 + def active_controller_action
34 + #so that we can override this value inside each action
35 + @active_controller = controller_name
36 + @active_action = action_name
37 + end
38 +
32 39 def admin_authorization
33 40 return false unless check_valid_login
34 41 user = User.includes(:roles).find(session[:user_id])
@@ -1,38 +1,49
1 1 class ProblemsController < ApplicationController
2 2
3 + include ActiveStorage::SetCurrent
4 +
3 5 before_action :admin_authorization, except: [:stat]
6 + before_action :set_problem, only: [:show, :edit, :update, :destroy, :get_statement, :toggle, :toggle_test, :toggle_view_testcase, :stat]
4 7 before_action only: [:stat] do
5 8 authorization_by_roles(['admin','ta'])
6 9 end
7 10
11 +
8 12 def index
9 13 @problems = Problem.order(date_added: :desc)
10 14 end
11 15
12 16
13 17 def show
14 - @problem = Problem.find(params[:id])
18 + end
19 +
20 + #get statement download link
21 + def get_statement
22 + unless @current_user.can_view_problem? @problem
23 + redirect_to list_main_path, error: 'You are not authorized to access this file'
24 + return
25 + end
26 +
27 + if params[:ext]=='pdf'
28 + content_type = 'application/pdf'
29 + else
30 + content_type = 'application/octet-stream'
31 + end
32 +
33 + filename = @problem.statement.filename.to_s
34 + data =@problem.statement.download
35 +
36 + send_data data, stream: false, disposition: 'inline', filename: filename, type: content_type
15 37 end
16 38
17 39 def new
18 40 @problem = Problem.new
19 - @description = nil
20 41 end
21 42
22 43 def create
23 44 @problem = Problem.new(problem_params)
24 - @description = Description.new(description_params)
25 - if @description.body!=''
26 - if !@description.save
27 - render :action => new and return
28 - end
29 - else
30 - @description = nil
31 - end
32 - @problem.description = @description
33 45 if @problem.save
34 - flash[:notice] = 'Problem was successfully created.'
35 - redirect_to action: :index
46 + redirect_to action: :index, notice: 'Problem was successfully created.'
36 47 else
37 48 render :action => 'new'
38 49 end
@@ -56,63 +67,31
56 67 end
57 68
58 69 def edit
59 - @problem = Problem.find(params[:id])
60 70 @description = @problem.description
61 71 end
62 72
63 73 def update
64 - @problem = Problem.find(params[:id])
65 - @description = @problem.description
66 - if @description.nil? and params[:description][:body]!=''
67 - @description = Description.new(description_params)
68 - if !@description.save
69 - flash[:notice] = 'Error saving description'
70 - render :action => 'edit' and return
71 - end
72 - @problem.description = @description
73 - elsif @description
74 - if !@description.update(description_params)
75 - flash[:notice] = 'Error saving description'
76 - render :action => 'edit' and return
77 - end
78 - end
79 - if params[:file] and params[:file].content_type != 'application/pdf'
80 - flash[:notice] = 'Error: Uploaded file is not PDF'
81 - render :action => 'edit' and return
74 + if problem_params[:statement] && problem_params[:statement].content_type != 'application/pdf'
75 + flash[:error] = 'Error: Uploaded file is not PDF'
76 + render :action => 'edit'
77 + return
82 78 end
83 79 if @problem.update(problem_params)
84 80 flash[:notice] = 'Problem was successfully updated.'
85 - unless params[:file] == nil or params[:file] == ''
86 - flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
87 - out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
88 - if not FileTest.exists? out_dirname
89 - Dir.mkdir out_dirname
90 - end
91 -
92 - out_filename = "#{out_dirname}/#{@problem.name}.pdf"
93 - if FileTest.exists? out_filename
94 - File.delete out_filename
95 - end
96 -
97 - File.open(out_filename,"wb") do |file|
98 - file.write(params[:file].read)
99 - end
100 - @problem.description_filename = "#{@problem.name}.pdf"
81 + flash[:notice] += 'A new statement PDF is uploaded' if problem_params[:statement]
101 82 @problem.save
102 - end
103 - redirect_to :action => 'show', :id => @problem
83 + redirect_to edit_problem_path(@problem)
104 84 else
105 85 render :action => 'edit'
106 86 end
107 87 end
108 88
109 89 def destroy
110 - p = Problem.find(params[:id]).destroy
90 + @problem.destroy
111 91 redirect_to action: :index
112 92 end
113 93
114 94 def toggle
115 - @problem = Problem.find(params[:id])
116 95 @problem.update(available: !(@problem.available) )
117 96 respond_to do |format|
118 97 format.js { }
@@ -120,7 +99,6
120 99 end
121 100
122 101 def toggle_test
123 - @problem = Problem.find(params[:id])
124 102 @problem.update(test_allowed: !(@problem.test_allowed?) )
125 103 respond_to do |format|
126 104 format.js { }
@@ -128,7 +106,6
128 106 end
129 107
130 108 def toggle_view_testcase
131 - @problem = Problem.find(params[:id])
132 109 @problem.update(view_testcase: !(@problem.view_testcase?) )
133 110 respond_to do |format|
134 111 format.js { }
@@ -152,7 +129,6
152 129 end
153 130
154 131 def stat
155 - @problem = Problem.find(params[:id])
156 132 unless @problem.available or session[:admin]
157 133 redirect_to :controller => 'main', :action => 'list'
158 134 return
@@ -295,8 +271,13
295 271
296 272 private
297 273
274 + def set_problem
275 + @problem = Problem.find(params[:id])
276 + end
277 +
298 278 def problem_params
299 - params.require(:problem).permit(:name, :full_name, :full_score, :change_date_added, :date_added, :available, :test_allowed,:output_only, :url, :description, tag_ids:[])
279 + params.require(:problem).permit(:name, :full_name, :full_score, :change_date_added, :date_added, :available,
280 + :test_allowed, :output_only, :url, :description, :statement, :description, tag_ids:[])
300 281 end
301 282
302 283 def description_params
@@ -209,4 +209,15
209 209 BOOTSTRAP_FLASH_MSG.fetch(flash_type.to_sym, flash_type.to_s)
210 210 end
211 211
212 + def active_class_when(options = {},cname = @active_controller, aname = @active_action)
213 + class_name = ' active '
214 + ok = true
215 + options.each do |k,v|
216 + ok = false if k == :controller && v.to_s != cname
217 + ok = false if k == :action && v.to_s != aname
212 218 end
219 + return class_name if ok && options.size > 0
220 + return ''
221 + end
222 +
223 + end
@@ -1,6 +1,7
1 1 class Problem < ApplicationRecord
2 2
3 - belongs_to :description
3 + #belongs_to :description
4 +
4 5 has_and_belongs_to_many :contests, :uniq => true
5 6
6 7 #has_and_belongs_to_many :groups
@@ -24,6 +25,9
24 25 DEFAULT_TIME_LIMIT = 1
25 26 DEFAULT_MEMORY_LIMIT = 32
26 27
28 + has_one_attached :statement
29 + has_many_attached :attachments
30 +
27 31 def get_jschart_history
28 32 start = 4.month.ago.beginning_of_day
29 33 start_date = start.to_date
@@ -11,14 +11,14
11 11 / submission
12 12 - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user))
13 13 %li.nav-item.dropdown.mx-2
14 - %a.nav-link.dropdown-toggle{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {expanded:"false"}, role: "button"}
14 + %a.nav-link.dropdown-toggle.active-with-children{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {expanded:"false"}, role: "button"}
15 15 = "#{I18n.t 'menu.submissions'}"
16 16 %ul.dropdown-menu
17 - %li= link_to 'View', submissions_path, class:'dropdown-item'
17 + %li= link_to 'View', submissions_path, class: 'dropdown-item '+active_class_when(controller: :submissions)
18 18 %li= link_to 'Self Test', test_index_path, class:'dropdown-item'
19 19 / hall of fame
20 20 - if GraderConfiguration['right.user_hall_of_fame']
21 - %li= link_to "#{I18n.t 'menu.hall_of_fame'}", problem_hof_report_path, class: 'nav-link mx-2'
21 + %li= link_to "#{I18n.t 'menu.hall_of_fame'}", problem_hof_report_path, class: 'nav-link mx-2'+active_class_when(controller: :report, action: :problem_hof)
22 22 / display MODE button (with countdown in contest mode)
23 23 - if GraderConfiguration.analysis_mode?
24 24 %div.btn.btn-success#countdown= "ANALYSIS MODE"
@@ -35,33 +35,33
35 35 - if (@current_user!=nil) and (session[:admin])
36 36 / management
37 37 %li.nav-item.dropdown.mx-2
38 - %a.nav-link.dropdown-toggle{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
38 + %a.nav-link.dropdown-toggle.active-with-children{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
39 39 Manage
40 40 %ul.dropdown-menu
41 - %li= link_to 'Announcements', announcements_path, class: 'dropdown-item'
42 - %li= link_to 'Problems', problems_path, class: 'dropdown-item'
43 - %li= link_to 'Tags', tags_path, class: 'dropdown-item'
44 - %li= link_to 'Users', user_admin_index_path, class: 'dropdown-item'
45 - %li= link_to 'User Groups', groups_path, class: 'dropdown-item'
46 - %li= link_to 'Graders', graders_list_path, class: 'dropdown-item'
47 - %li= link_to 'Message ', console_messages_path, class: 'dropdown-item'
41 + %li= link_to 'Announcements', announcements_path, class: 'dropdown-item'+active_class_when(controller: :announcements)
42 + %li= link_to 'Problems', problems_path, class: 'dropdown-item'+active_class_when(controller: :problems)
43 + %li= link_to 'Tags', tags_path, class: 'dropdown-item'+active_class_when(controller: :tags)
44 + %li= link_to 'Users', user_admin_index_path, class: 'dropdown-item'+active_class_when(controller: :user_admin)
45 + %li= link_to 'User Groups', groups_path, class: 'dropdown-item'+active_class_when(controller: :groups)
46 + %li= link_to 'Graders', graders_list_path, class: 'dropdown-item'+active_class_when(controller: :graders)
47 + %li= link_to 'Message ', console_messages_path, class: 'dropdown-item'+active_class_when(controller: :messages)
48 48 %li
49 49 %hr.dropdown-divider
50 - %li= link_to 'System config', grader_configuration_index_path, class: 'dropdown-item'
50 + %li= link_to 'System config', grader_configuration_index_path, class: 'dropdown-item'+active_class_when(controller: :grader_configuration)
51 51 %li
52 52 %hr.dropdown-divider
53 - %li= link_to 'Sites', sites_path, class: 'dropdown-item'
54 - %li= link_to 'Contests', contest_management_index_path, class: 'dropdown-item'
53 + %li= link_to 'Sites', sites_path, class: 'dropdown-item'+active_class_when(controller: :sites)
54 + %li= link_to 'Contests', contest_management_index_path, class: 'dropdown-item'+active_class_when(controller: :contest_management)
55 55 -#
56 56 / report
57 57 %li.nav-item.dropdown.mx-2
58 - %a.nav-link.dropdown-toggle{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
58 + %a.nav-link.dropdown-toggle.active-with-children{href: '#', 'data-bs': {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
59 59 Report
60 60 %ul.dropdown-menu
61 - %li= link_to 'Current Score', current_score_report_path, class: 'dropdown-item'
62 - %li= link_to 'Score Report', max_score_report_path, class: 'dropdown-item'
63 - %li= link_to 'Submission Report', submission_report_path, class: 'dropdown-item'
64 - %li= link_to 'Login Report', login_report_path, class: 'dropdown-item'
61 + %li= link_to 'Current Score', current_score_report_path, class: 'dropdown-item'+active_class_when(controller: :report, action: :current_score)
62 + %li= link_to 'Score Report', max_score_report_path, class: 'dropdown-item'+active_class_when(controller: :report, action: :max_score)
63 + %li= link_to 'Submission Report', submission_report_path, class: 'dropdown-item'+active_class_when(controller: :report, action: :submission)
64 + %li= link_to 'Login Report', login_report_path, class: 'dropdown-item'+active_class_when(controller: :report, action: :login)
65 65 - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0
66 66 =link_to "#{ungraded} backlogs!",
67 67 graders_list_path,
@@ -85,3 +85,9
85 85 %a.nav-link{href: login_main_path}
86 86 %span.mi.mi-bs.md-18 exit_to_app
87 87 = @current_user.full_name
88 + :javascript
89 + $('.active-with-children').each( (index,obj) => {
90 + if ($(obj).siblings('.dropdown-menu').has('.active').length > 0) {
91 + $(obj).addClass('active')
92 + }
93 + } )
@@ -1,4 +1,27
1 + = simple_form_for problem do |form|
2 + .row
3 + .col-md-6
4 + = form.input :name
5 + = form.input :full_name
6 + = form.input :full_score
7 + = form.input :tag_ids, collection: Tag.all, class: 'select2'
8 + = form.input :date_added
9 + = form.input :available
10 + = form.input :test_allowed
11 + = form.input :output_only
12 + = form.input :description, as: :text
13 + = form.input :markdown
14 + = form.input :url
15 + = form.input :statement
16 + %p
17 + - if @problem.statement.attached?
18 + %a{href: get_statement_problem_path(@problem)} [Download current Statement]
19 + - else
20 + no statement attached to this problem
21 + = form.submit :submit, class: 'btn btn-primary'
22 + -#
1 23 = error_messages_for 'problem'
24 +
2 25 / [form:problem]
3 26 .form-group
4 27 %label{:for => "problem_name"} Name
@@ -1,14 +1,6
1 - .container-fluid
2 - = form_for @problem,url:{action: 'update'},html: {multipart: true} do
3 - .row
4 - .col-md-6
5 - %h1 Editing problem
6 - = render :partial => 'form'
7 - .row
1 + %h1 Editing Problem
2 +
3 + = render 'form', problem: @problem
4 + .row.my-3
8 5 .col-md-4
9 - = submit_tag 'Edit', class: 'btn btn-primary btn-block'
10 - .col-md-4
11 - = link_to 'Show', {:action => 'show', :id => @problem}, class: 'btn btn-default btn-block'
12 - .col-md-4
13 - = link_to 'Back', problems_path, class: 'btn btn-default btn-block'
14 - .div{style: 'height: 5em'}
6 + = link_to 'Back', problems_path, class: 'btn btn-secondary'
@@ -37,6 +37,9
37 37 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
38 38 - if GraderConfiguration.multicontests?
39 39 %th Contests
40 + %th.text-center
41 + %th.text-center
42 + %th.text-center
40 43 - for problem in @problems
41 44 %tr{:class => "#{(problem.available) ? "bg-success bg-opacity-25" : "bg-opacity-25"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
42 45 - @problem=problem
@@ -58,7 +61,6
58 61 %td
59 62 = problem.contests.collect { |c| c.name }.join(', ')
60 63 %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-sm btn-block'
61 - %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-sm btn-block'
62 64 %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-sm btn-block'
63 65 %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-sm btn-block'
64 66 %br/
@@ -112,7 +112,7
112 112 # You can define the class to use on all labels. Default is nil.
113 113 # config.label_class = nil
114 114
115 - # You can define the default class to be used on forms. Can be overriden
115 + # You can define the default class to be used on forms. Can be overridden
116 116 # with `html: { :class }`. Defaulting to none.
117 117 # config.default_form_class = nil
118 118
@@ -1,10 +1,8
1 1 # frozen_string_literal: true
2 2
3 - # Please do not make direct changes to this file!
4 - # This generator is maintained by the community around simple_form-bootstrap:
5 - # https://github.com/rafaelfranca/simple_form-bootstrap
6 - # All future development, tests, and organization should happen there.
7 - # Background history: https://github.com/heartcombo/simple_form/issues/1561
3 + # These defaults are defined and maintained by the community at
4 + # https://github.com/heartcombo/simple_form-bootstrap
5 + # Please submit feedback, changes and tests only there.
8 6
9 7 # Uncomment this and change the path if necessary to include your own
10 8 # components.
@@ -43,13 +41,13
43 41
44 42 # add validation classes to `input_field`
45 43 config.input_field_error_class = 'is-invalid'
46 - config.input_field_valid_class = 'is-valid'
44 + config.input_field_valid_class = 'is-valid-xxx'
47 45
48 46
49 47 # vertical forms
50 48 #
51 49 # vertical default_wrapper
52 - config.wrappers :vertical_form, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
50 + config.wrappers :vertical_form, class: 'mb-3' do |b|
53 51 b.use :html5
54 52 b.use :placeholder
55 53 b.optional :maxlength
@@ -57,90 +55,100
57 55 b.optional :pattern
58 56 b.optional :min_max
59 57 b.optional :readonly
60 - b.use :label
61 - b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
62 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
63 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
58 + b.use :label, class: 'form-label'
59 + b.use :input, class: 'form-control', error_class: 'is-invalid'
60 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
61 + b.use :hint, wrap_with: { class: 'form-text' }
64 62 end
65 63
66 64 # vertical input for boolean
67 - config.wrappers :vertical_boolean, tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
65 + config.wrappers :vertical_boolean, tag: 'fieldset', class: 'mb-3' do |b|
68 66 b.use :html5
69 67 b.optional :readonly
70 - b.wrapper :form_check_wrapper, tag: 'div', class: 'form-check' do |bb|
71 - bb.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
68 + b.wrapper :form_check_wrapper, class: 'form-check' do |bb|
69 + bb.use :input, class: 'form-check-input', error_class: 'is-invalid'
72 70 bb.use :label, class: 'form-check-label'
73 - bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
74 - bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
71 + bb.use :full_error, wrap_with: { class: 'invalid-feedback' }
72 + bb.use :hint, wrap_with: { class: 'form-text' }
75 73 end
76 74 end
77 75
78 76 # vertical input for radio buttons and check boxes
79 - config.wrappers :vertical_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
77 + config.wrappers :vertical_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', tag: 'fieldset', class: 'mb-3' do |b|
80 78 b.use :html5
81 79 b.optional :readonly
82 80 b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
83 81 ba.use :label_text
84 82 end
85 - b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
86 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
87 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
83 + b.use :input, class: 'form-check-input', error_class: 'is-invalid'
84 + b.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
85 + b.use :hint, wrap_with: { class: 'form-text' }
88 86 end
89 87
90 88 # vertical input for inline radio buttons and check boxes
91 - config.wrappers :vertical_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
89 + config.wrappers :vertical_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', tag: 'fieldset', class: 'mb-3' do |b|
92 90 b.use :html5
93 91 b.optional :readonly
94 92 b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
95 93 ba.use :label_text
96 94 end
97 - b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
98 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
99 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
95 + b.use :input, class: 'form-check-input', error_class: 'is-invalid'
96 + b.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
97 + b.use :hint, wrap_with: { class: 'form-text' }
100 98 end
101 99
102 100 # vertical file input
103 - config.wrappers :vertical_file, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
101 + config.wrappers :vertical_file, class: 'mb-3' do |b|
104 102 b.use :html5
105 103 b.use :placeholder
106 104 b.optional :maxlength
107 105 b.optional :minlength
108 106 b.optional :readonly
109 - b.use :label
110 - b.use :input, class: 'form-control-file', error_class: 'is-invalid', valid_class: 'is-valid'
111 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
112 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
107 + b.use :label, class: 'form-label'
108 + b.use :input, class: 'form-control', error_class: 'is-invalid'
109 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
110 + b.use :hint, wrap_with: { class: 'form-text' }
111 + end
112 +
113 + # vertical select input
114 + config.wrappers :vertical_select, class: 'mb-3' do |b|
115 + b.use :html5
116 + b.optional :readonly
117 + b.use :label, class: 'form-label'
118 + b.use :input, class: 'form-select', error_class: 'is-invalid'
119 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
120 + b.use :hint, wrap_with: { class: 'form-text' }
113 121 end
114 122
115 123 # vertical multi select
116 - config.wrappers :vertical_multi_select, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
124 + config.wrappers :vertical_multi_select, class: 'mb-3' do |b|
117 125 b.use :html5
118 126 b.optional :readonly
119 - b.use :label
120 - b.wrapper tag: 'div', class: 'd-flex flex-row justify-content-between align-items-center' do |ba|
121 - ba.use :input, class: 'form-control mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
127 + b.use :label, class: 'form-label'
128 + b.wrapper class: 'd-flex flex-row justify-content-between align-items-center' do |ba|
129 + ba.use :input, class: 'form-select mx-1', error_class: 'is-invalid'
122 130 end
123 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
124 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
131 + b.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
132 + b.use :hint, wrap_with: { class: 'form-text' }
125 133 end
126 134
127 135 # vertical range input
128 - config.wrappers :vertical_range, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
136 + config.wrappers :vertical_range, class: 'mb-3' do |b|
129 137 b.use :html5
130 138 b.use :placeholder
131 139 b.optional :readonly
132 140 b.optional :step
133 - b.use :label
134 - b.use :input, class: 'form-control-range', error_class: 'is-invalid', valid_class: 'is-valid'
135 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
136 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
141 + b.use :label, class: 'form-label'
142 + b.use :input, class: 'form-range', error_class: 'is-invalid'
143 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
144 + b.use :hint, wrap_with: { class: 'form-text' }
137 145 end
138 146
139 147
140 148 # horizontal forms
141 149 #
142 150 # horizontal default_wrapper
143 - config.wrappers :horizontal_form, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
151 + config.wrappers :horizontal_form, class: 'row mb-3' do |b|
144 152 b.use :html5
145 153 b.use :placeholder
146 154 b.optional :maxlength
@@ -149,94 +157,103
149 157 b.optional :min_max
150 158 b.optional :readonly
151 159 b.use :label, class: 'col-sm-3 col-form-label'
152 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
153 - ba.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
154 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
155 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
160 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
161 + ba.use :input, class: 'form-control', error_class: 'is-invalid'
162 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
163 + ba.use :hint, wrap_with: { class: 'form-text' }
156 164 end
157 165 end
158 166
159 167 # horizontal input for boolean
160 - config.wrappers :horizontal_boolean, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
168 + config.wrappers :horizontal_boolean, class: 'row mb-3' do |b|
161 169 b.use :html5
162 170 b.optional :readonly
163 - b.wrapper tag: 'label', class: 'col-sm-3' do |ba|
164 - ba.use :label_text
165 - end
166 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |wr|
167 - wr.wrapper :form_check_wrapper, tag: 'div', class: 'form-check' do |bb|
168 - bb.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
171 + b.wrapper :grid_wrapper, class: 'col-sm-9 offset-sm-3' do |wr|
172 + wr.wrapper :form_check_wrapper, class: 'form-check' do |bb|
173 + bb.use :input, class: 'form-check-input', error_class: 'is-invalid'
169 174 bb.use :label, class: 'form-check-label'
170 - bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
171 - bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
175 + bb.use :full_error, wrap_with: { class: 'invalid-feedback' }
176 + bb.use :hint, wrap_with: { class: 'form-text' }
172 177 end
173 178 end
174 179 end
175 180
176 181 # horizontal input for radio buttons and check boxes
177 - config.wrappers :horizontal_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
182 + config.wrappers :horizontal_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', class: 'row mb-3' do |b|
178 183 b.use :html5
179 184 b.optional :readonly
180 185 b.use :label, class: 'col-sm-3 col-form-label pt-0'
181 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
182 - ba.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
183 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
184 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
186 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
187 + ba.use :input, class: 'form-check-input', error_class: 'is-invalid'
188 + ba.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
189 + ba.use :hint, wrap_with: { class: 'form-text' }
185 190 end
186 191 end
187 192
188 193 # horizontal input for inline radio buttons and check boxes
189 - config.wrappers :horizontal_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
194 + config.wrappers :horizontal_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', class: 'row mb-3' do |b|
190 195 b.use :html5
191 196 b.optional :readonly
192 197 b.use :label, class: 'col-sm-3 col-form-label pt-0'
193 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
194 - ba.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
195 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
196 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
198 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
199 + ba.use :input, class: 'form-check-input', error_class: 'is-invalid'
200 + ba.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
201 + ba.use :hint, wrap_with: { class: 'form-text' }
197 202 end
198 203 end
199 204
200 205 # horizontal file input
201 - config.wrappers :horizontal_file, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
206 + config.wrappers :horizontal_file, class: 'row mb-3' do |b|
202 207 b.use :html5
203 208 b.use :placeholder
204 209 b.optional :maxlength
205 210 b.optional :minlength
206 211 b.optional :readonly
207 212 b.use :label, class: 'col-sm-3 col-form-label'
208 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
209 - ba.use :input, error_class: 'is-invalid', valid_class: 'is-valid'
210 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
211 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
213 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
214 + ba.use :input, class: 'form-control', error_class: 'is-invalid'
215 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
216 + ba.use :hint, wrap_with: { class: 'form-text' }
217 + end
218 + end
219 +
220 + # horizontal select input
221 + config.wrappers :horizontal_select, class: 'row mb-3' do |b|
222 + b.use :html5
223 + b.optional :readonly
224 + b.use :label, class: 'col-sm-3 col-form-label'
225 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
226 + ba.use :input, class: 'form-select', error_class: 'is-invalid'
227 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
228 + ba.use :hint, wrap_with: { class: 'form-text' }
212 229 end
213 230 end
214 231
215 232 # horizontal multi select
216 - config.wrappers :horizontal_multi_select, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
233 + config.wrappers :horizontal_multi_select, class: 'row mb-3' do |b|
217 234 b.use :html5
218 235 b.optional :readonly
219 236 b.use :label, class: 'col-sm-3 col-form-label'
220 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
221 - ba.wrapper tag: 'div', class: 'd-flex flex-row justify-content-between align-items-center' do |bb|
222 - bb.use :input, class: 'form-control mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
237 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
238 + ba.wrapper class: 'd-flex flex-row justify-content-between align-items-center' do |bb|
239 + bb.use :input, class: 'form-select mx-1', error_class: 'is-invalid'
223 240 end
224 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
225 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
241 + ba.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
242 + ba.use :hint, wrap_with: { class: 'form-text' }
226 243 end
227 244 end
228 245
229 246 # horizontal range input
230 - config.wrappers :horizontal_range, tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
247 + config.wrappers :horizontal_range, class: 'row mb-3' do |b|
231 248 b.use :html5
232 249 b.use :placeholder
233 250 b.optional :readonly
234 251 b.optional :step
235 - b.use :label, class: 'col-sm-3 col-form-label'
236 - b.wrapper :grid_wrapper, tag: 'div', class: 'col-sm-9' do |ba|
237 - ba.use :input, class: 'form-control-range', error_class: 'is-invalid', valid_class: 'is-valid'
238 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
239 - ba.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
252 + b.use :label, class: 'col-sm-3 col-form-label pt-0'
253 + b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
254 + ba.use :input, class: 'form-range', error_class: 'is-invalid'
255 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
256 + ba.use :hint, wrap_with: { class: 'form-text' }
240 257 end
241 258 end
242 259
@@ -244,7 +261,7
244 261 # inline forms
245 262 #
246 263 # inline default_wrapper
247 - config.wrappers :inline_form, tag: 'span', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
264 + config.wrappers :inline_form, class: 'col-12' do |b|
248 265 b.use :html5
249 266 b.use :placeholder
250 267 b.optional :maxlength
@@ -252,140 +269,44
252 269 b.optional :pattern
253 270 b.optional :min_max
254 271 b.optional :readonly
255 - b.use :label, class: 'sr-only'
272 + b.use :label, class: 'visually-hidden'
256 273
257 - b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
258 - b.use :error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
259 - b.optional :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
274 + b.use :input, class: 'form-control', error_class: 'is-invalid'
275 + b.use :error, wrap_with: { class: 'invalid-feedback' }
276 + b.optional :hint, wrap_with: { class: 'form-text' }
260 277 end
261 278
262 279 # inline input for boolean
263 - config.wrappers :inline_boolean, tag: 'span', class: 'form-check mb-2 mr-sm-2', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
280 + config.wrappers :inline_boolean, class: 'col-12' do |b|
264 281 b.use :html5
265 282 b.optional :readonly
266 - b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
267 - b.use :label, class: 'form-check-label'
268 - b.use :error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
269 - b.optional :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
283 + b.wrapper :form_check_wrapper, class: 'form-check' do |bb|
284 + bb.use :input, class: 'form-check-input', error_class: 'is-invalid'
285 + bb.use :label, class: 'form-check-label'
286 + bb.use :error, wrap_with: { class: 'invalid-feedback' }
287 + bb.optional :hint, wrap_with: { class: 'form-text' }
288 + end
270 289 end
271 290
272 291
273 292 # bootstrap custom forms
274 293 #
275 - # custom input for boolean
276 - config.wrappers :custom_boolean, tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
277 - b.use :html5
278 - b.optional :readonly
279 - b.wrapper :form_check_wrapper, tag: 'div', class: 'custom-control custom-checkbox' do |bb|
280 - bb.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
281 - bb.use :label, class: 'custom-control-label'
282 - bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
283 - bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
284 - end
285 - end
286 -
287 294 # custom input switch for boolean
288 - config.wrappers :custom_boolean_switch, tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
289 - b.use :html5
290 - b.optional :readonly
291 - b.wrapper :form_check_wrapper, tag: 'div', class: 'custom-control custom-switch' do |bb|
292 - bb.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
293 - bb.use :label, class: 'custom-control-label'
294 - bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
295 - bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
296 - end
297 - end
298 -
299 - # custom input for radio buttons and check boxes
300 - config.wrappers :custom_collection, item_wrapper_class: 'custom-control', item_label_class: 'custom-control-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
301 - b.use :html5
302 - b.optional :readonly
303 - b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
304 - ba.use :label_text
305 - end
306 - b.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
307 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
308 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
309 - end
310 -
311 - # custom input for inline radio buttons and check boxes
312 - config.wrappers :custom_collection_inline, item_wrapper_class: 'custom-control custom-control-inline', item_label_class: 'custom-control-label', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
295 + config.wrappers :custom_boolean_switch, class: 'mb-3' do |b|
313 296 b.use :html5
314 297 b.optional :readonly
315 - b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
316 - ba.use :label_text
317 - end
318 - b.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
319 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
320 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
321 - end
322 -
323 - # custom file input
324 - config.wrappers :custom_file, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
325 - b.use :html5
326 - b.use :placeholder
327 - b.optional :maxlength
328 - b.optional :minlength
329 - b.optional :readonly
330 - b.use :label
331 - b.wrapper :custom_file_wrapper, tag: 'div', class: 'custom-file' do |ba|
332 - ba.use :input, class: 'custom-file-input', error_class: 'is-invalid', valid_class: 'is-valid'
333 - ba.use :label, class: 'custom-file-label'
334 - ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
335 - end
336 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
298 + b.wrapper :form_check_wrapper, tag: 'div', class: 'form-check form-switch' do |bb|
299 + bb.use :input, class: 'form-check-input', error_class: 'is-invalid'
300 + bb.use :label, class: 'form-check-label'
301 + bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
302 + bb.use :hint, wrap_with: { class: 'form-text' }
337 303 end
338 -
339 - # custom multi select
340 - config.wrappers :custom_multi_select, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
341 - b.use :html5
342 - b.optional :readonly
343 - b.use :label
344 - b.wrapper tag: 'div', class: 'd-flex flex-row justify-content-between align-items-center' do |ba|
345 - ba.use :input, class: 'custom-select mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
346 - end
347 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
348 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
349 - end
350 -
351 - # custom range input
352 - config.wrappers :custom_range, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
353 - b.use :html5
354 - b.use :placeholder
355 - b.optional :readonly
356 - b.optional :step
357 - b.use :label
358 - b.use :input, class: 'custom-range', error_class: 'is-invalid', valid_class: 'is-valid'
359 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
360 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
361 304 end
362 305
363 306
364 307 # Input Group - custom component
365 - # see example app and config at https://github.com/rafaelfranca/simple_form-bootstrap
366 - # config.wrappers :input_group, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
367 - # b.use :html5
368 - # b.use :placeholder
369 - # b.optional :maxlength
370 - # b.optional :minlength
371 - # b.optional :pattern
372 - # b.optional :min_max
373 - # b.optional :readonly
374 - # b.use :label
375 - # b.wrapper :input_group_tag, tag: 'div', class: 'input-group' do |ba|
376 - # ba.optional :prepend
377 - # ba.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
378 - # ba.optional :append
379 - # end
380 - # b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
381 - # b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
382 - # end
383 -
384 -
385 - # Floating Labels form
386 - #
387 - # floating labels default_wrapper
388 - config.wrappers :floating_labels_form, tag: 'div', class: 'form-label-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
308 + # see example app and config at https://github.com/heartcombo/simple_form-bootstrap
309 + config.wrappers :input_group, class: 'mb-3' do |b|
389 310 b.use :html5
390 311 b.use :placeholder
391 312 b.optional :maxlength
@@ -393,20 +314,42
393 314 b.optional :pattern
394 315 b.optional :min_max
395 316 b.optional :readonly
396 - b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
317 + b.use :label, class: 'form-label'
318 + b.wrapper :input_group_tag, class: 'input-group' do |ba|
319 + ba.optional :prepend
320 + ba.use :input, class: 'form-control', error_class: 'is-invalid'
321 + ba.optional :append
322 + ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
323 + end
324 + b.use :hint, wrap_with: { class: 'form-text' }
325 + end
326 +
327 +
328 + # Floating Labels form
329 + #
330 + # floating labels default_wrapper
331 + config.wrappers :floating_labels_form, class: 'form-floating mb-3' do |b|
332 + b.use :html5
333 + b.use :placeholder
334 + b.optional :maxlength
335 + b.optional :minlength
336 + b.optional :pattern
337 + b.optional :min_max
338 + b.optional :readonly
339 + b.use :input, class: 'form-control', error_class: 'is-invalid'
397 340 b.use :label
398 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
399 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
341 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
342 + b.use :hint, wrap_with: { class: 'form-text' }
400 343 end
401 344
402 345 # custom multi select
403 - config.wrappers :floating_labels_select, tag: 'div', class: 'form-label-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
346 + config.wrappers :floating_labels_select, class: 'form-floating mb-3' do |b|
404 347 b.use :html5
405 348 b.optional :readonly
406 - b.use :input, class: 'custom-select', error_class: 'is-invalid', valid_class: 'is-valid'
349 + b.use :input, class: 'form-select', error_class: 'is-invalid'
407 350 b.use :label
408 - b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
409 - b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
351 + b.use :full_error, wrap_with: { class: 'invalid-feedback' }
352 + b.use :hint, wrap_with: { class: 'form-text' }
410 353 end
411 354
412 355
@@ -423,18 +366,7
423 366 file: :vertical_file,
424 367 radio_buttons: :vertical_collection,
425 368 range: :vertical_range,
426 - time: :vertical_multi_select
369 + time: :vertical_multi_select,
370 + select: :vertical_select
427 371 }
428 -
429 - # enable custom form wrappers
430 - # config.wrapper_mappings = {
431 - # boolean: :custom_boolean,
432 - # check_boxes: :custom_collection,
433 - # date: :custom_multi_select,
434 - # datetime: :custom_multi_select,
435 - # file: :custom_file,
436 - # radio_buttons: :custom_collection,
437 - # range: :custom_range,
438 - # time: :custom_multi_select
439 - # }
440 372 end
@@ -34,6 +34,7
34 34 get 'toggle_test'
35 35 get 'toggle_view_testcase'
36 36 get 'stat'
37 + get 'get_statement'
37 38 end
38 39 collection do
39 40 get 'turn_all_off'
@@ -10,10 +10,38
10 10 #
11 11 # It's strongly recommended that you check this file into your version control system.
12 12
13 - ActiveRecord::Schema[7.0].define(version: 2022_02_04_080936) do
14 - create_table "announcements", id: :integer, charset: "utf8", force: :cascade do |t|
13 + ActiveRecord::Schema[7.0].define(version: 2022_09_27_074644) do
14 + create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
15 + t.string "name", null: false
16 + t.string "record_type", null: false
17 + t.bigint "record_id", null: false
18 + t.bigint "blob_id", null: false
19 + t.datetime "created_at", null: false
20 + t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
21 + t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
22 + end
23 +
24 + create_table "active_storage_blobs", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
25 + t.string "key", null: false
26 + t.string "filename", null: false
27 + t.string "content_type"
28 + t.text "metadata"
29 + t.string "service_name", null: false
30 + t.bigint "byte_size", null: false
31 + t.string "checksum"
32 + t.datetime "created_at", null: false
33 + t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
34 + end
35 +
36 + create_table "active_storage_variant_records", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
37 + t.bigint "blob_id", null: false
38 + t.string "variation_digest", null: false
39 + t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
40 + end
41 +
42 + create_table "announcements", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
15 43 t.string "author"
16 - t.text "body", size: :medium
44 + t.text "body"
17 45 t.boolean "published"
18 46 t.datetime "created_at", precision: nil, null: false
19 47 t.datetime "updated_at", precision: nil, null: false
@@ -24,7 +52,7
24 52 t.boolean "on_nav_bar", default: false
25 53 end
26 54
27 - create_table "contests", id: :integer, charset: "utf8", force: :cascade do |t|
55 + create_table "contests", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
28 56 t.string "title"
29 57 t.boolean "enabled"
30 58 t.datetime "created_at", precision: nil, null: false
@@ -32,39 +60,39
32 60 t.string "name"
33 61 end
34 62
35 - create_table "contests_problems", id: false, charset: "utf8", force: :cascade do |t|
63 + create_table "contests_problems", id: false, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
36 64 t.integer "contest_id"
37 65 t.integer "problem_id"
38 66 end
39 67
40 - create_table "contests_users", id: false, charset: "utf8", force: :cascade do |t|
68 + create_table "contests_users", id: false, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
41 69 t.integer "contest_id"
42 70 t.integer "user_id"
43 71 end
44 72
45 - create_table "countries", id: :integer, charset: "utf8", force: :cascade do |t|
73 + create_table "countries", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
46 74 t.string "name"
47 75 t.datetime "created_at", precision: nil, null: false
48 76 t.datetime "updated_at", precision: nil, null: false
49 77 end
50 78
51 - create_table "descriptions", id: :integer, charset: "utf8", force: :cascade do |t|
52 - t.text "body", size: :medium
79 + create_table "descriptions", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
80 + t.text "body"
53 81 t.boolean "markdowned"
54 82 t.datetime "created_at", precision: nil, null: false
55 83 t.datetime "updated_at", precision: nil, null: false
56 84 end
57 85
58 - create_table "grader_configurations", id: :integer, charset: "utf8", force: :cascade do |t|
86 + create_table "grader_configurations", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
59 87 t.string "key"
60 88 t.string "value_type"
61 89 t.string "value"
62 90 t.datetime "created_at", precision: nil, null: false
63 91 t.datetime "updated_at", precision: nil, null: false
64 - t.text "description", size: :medium
92 + t.text "description"
65 93 end
66 94
67 - create_table "grader_processes", id: :integer, charset: "utf8", force: :cascade do |t|
95 + create_table "grader_processes", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
68 96 t.string "host"
69 97 t.integer "pid"
70 98 t.string "mode"
@@ -95,7 +123,7
95 123 t.index ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id"
96 124 end
97 125
98 - create_table "heart_beats", id: :integer, charset: "latin1", force: :cascade do |t|
126 + create_table "heart_beats", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
99 127 t.integer "user_id"
100 128 t.string "ip_address"
101 129 t.datetime "created_at", precision: nil, null: false
@@ -104,14 +132,14
104 132 t.index ["updated_at"], name: "index_heart_beats_on_updated_at"
105 133 end
106 134
107 - create_table "languages", id: :integer, charset: "utf8", force: :cascade do |t|
135 + create_table "languages", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
108 136 t.string "name", limit: 10
109 137 t.string "pretty_name"
110 138 t.string "ext", limit: 10
111 139 t.string "common_ext"
112 140 end
113 141
114 - create_table "logins", id: :integer, charset: "latin1", force: :cascade do |t|
142 + create_table "logins", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
115 143 t.integer "user_id"
116 144 t.string "ip_address"
117 145 t.datetime "created_at", precision: nil, null: false
@@ -119,18 +147,18
119 147 t.index ["user_id"], name: "index_logins_on_user_id"
120 148 end
121 149
122 - create_table "messages", id: :integer, charset: "utf8", force: :cascade do |t|
150 + create_table "messages", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
123 151 t.integer "sender_id"
124 152 t.integer "receiver_id"
125 153 t.integer "replying_message_id"
126 - t.text "body", size: :medium
154 + t.text "body"
127 155 t.boolean "replied"
128 156 t.datetime "created_at", precision: nil, null: false
129 157 t.datetime "updated_at", precision: nil, null: false
130 158 end
131 159
132 - create_table "problems", id: :bigint, default: nil, charset: "utf8", force: :cascade do |t|
133 - t.string "name", limit: 100
160 + create_table "problems", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
161 + t.string "name", limit: 30
134 162 t.string "full_name"
135 163 t.integer "full_score"
136 164 t.date "date_added"
@@ -142,47 +170,49
142 170 t.string "description_filename"
143 171 t.boolean "view_testcase"
144 172 t.integer "difficulty"
173 + t.text "description"
174 + t.boolean "markdown"
145 175 end
146 176
147 - create_table "problems_tags", id: :bigint, default: nil, charset: "latin1", force: :cascade do |t|
148 - t.bigint "problem_id"
177 + create_table "problems_tags", id: :integer, charset: "latin1", force: :cascade do |t|
178 + t.integer "problem_id"
149 179 t.integer "tag_id"
150 180 t.index ["problem_id", "tag_id"], name: "index_problems_tags_on_problem_id_and_tag_id", unique: true
151 181 t.index ["problem_id"], name: "index_problems_tags_on_problem_id"
152 182 t.index ["tag_id"], name: "index_problems_tags_on_tag_id"
153 183 end
154 184
155 - create_table "rights", id: :integer, charset: "utf8", force: :cascade do |t|
185 + create_table "rights", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
156 186 t.string "name"
157 187 t.string "controller"
158 188 t.string "action"
159 189 end
160 190
161 - create_table "rights_roles", id: false, charset: "utf8", force: :cascade do |t|
191 + create_table "rights_roles", id: false, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
162 192 t.integer "right_id"
163 193 t.integer "role_id"
164 194 t.index ["role_id"], name: "index_rights_roles_on_role_id"
165 195 end
166 196
167 - create_table "roles", id: :integer, charset: "utf8", force: :cascade do |t|
197 + create_table "roles", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
168 198 t.string "name"
169 199 end
170 200
171 - create_table "roles_users", id: false, charset: "utf8", force: :cascade do |t|
201 + create_table "roles_users", id: false, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
172 202 t.integer "role_id"
173 203 t.integer "user_id"
174 204 t.index ["user_id"], name: "index_roles_users_on_user_id"
175 205 end
176 206
177 - create_table "sessions", id: :integer, charset: "utf8", force: :cascade do |t|
207 + create_table "sessions", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
178 208 t.string "session_id"
179 - t.text "data", size: :medium
209 + t.text "data"
180 210 t.datetime "updated_at", precision: nil
181 211 t.index ["session_id"], name: "index_sessions_on_session_id"
182 212 t.index ["updated_at"], name: "index_sessions_on_updated_at"
183 213 end
184 214
185 - create_table "sites", id: :integer, charset: "utf8", force: :cascade do |t|
215 + create_table "sites", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
186 216 t.string "name"
187 217 t.boolean "started"
188 218 t.datetime "start_time", precision: nil
@@ -192,23 +222,14
192 222 t.string "password"
193 223 end
194 224
195 - create_table "solutions", charset: "latin1", force: :cascade do |t|
196 - t.string "solution"
197 - t.bigint "problem_id"
198 - t.bigint "submission_id"
199 - t.integer "type"
200 - t.index ["problem_id"], name: "index_solutions_on_problem_id"
201 - t.index ["submission_id"], name: "index_solutions_on_submission_id"
202 - end
203 -
204 - create_table "submission_view_logs", id: :integer, charset: "latin1", force: :cascade do |t|
225 + create_table "submission_view_logs", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
205 226 t.integer "user_id"
206 227 t.integer "submission_id"
207 228 t.datetime "created_at", precision: nil, null: false
208 229 t.datetime "updated_at", precision: nil, null: false
209 230 end
210 231
211 - create_table "submissions", id: :bigint, default: nil, charset: "utf8", force: :cascade do |t|
232 + create_table "submissions", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
212 233 t.integer "user_id"
213 234 t.integer "problem_id"
214 235 t.integer "language_id"
@@ -216,10 +237,10
216 237 t.binary "binary"
217 238 t.datetime "submitted_at", precision: nil
218 239 t.datetime "compiled_at", precision: nil
219 - t.text "compiler_message", size: :medium
240 + t.text "compiler_message"
220 241 t.datetime "graded_at", precision: nil
221 242 t.integer "points"
222 - t.text "grader_comment", size: :medium
243 + t.text "grader_comment"
223 244 t.integer "number"
224 245 t.string "source_filename"
225 246 t.float "max_runtime"
@@ -240,7 +261,7
240 261 t.datetime "updated_at", precision: nil, null: false
241 262 end
242 263
243 - create_table "tasks", id: :integer, charset: "utf8", force: :cascade do |t|
264 + create_table "tasks", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
244 265 t.integer "submission_id"
245 266 t.datetime "created_at", precision: nil
246 267 t.integer "status"
@@ -249,15 +270,15
249 270 t.index ["submission_id"], name: "index_tasks_on_submission_id"
250 271 end
251 272
252 - create_table "test_pairs", id: :integer, charset: "utf8", force: :cascade do |t|
273 + create_table "test_pairs", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
253 274 t.integer "problem_id"
254 - t.text "input", size: :long
255 - t.text "solution", size: :long
275 + t.text "input", size: :medium
276 + t.text "solution", size: :medium
256 277 t.datetime "created_at", precision: nil, null: false
257 278 t.datetime "updated_at", precision: nil, null: false
258 279 end
259 280
260 - create_table "test_requests", id: :integer, charset: "utf8", force: :cascade do |t|
281 + create_table "test_requests", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
261 282 t.integer "user_id"
262 283 t.integer "problem_id"
263 284 t.integer "submission_id"
@@ -268,7 +289,7
268 289 t.datetime "updated_at", precision: nil, null: false
269 290 t.datetime "submitted_at", precision: nil
270 291 t.datetime "compiled_at", precision: nil
271 - t.text "compiler_message", size: :medium
292 + t.text "compiler_message"
272 293 t.datetime "graded_at", precision: nil
273 294 t.string "grader_comment"
274 295 t.datetime "created_at", precision: nil, null: false
@@ -285,12 +306,12
285 306 t.integer "score"
286 307 t.text "input", size: :long
287 308 t.text "sol", size: :long
288 - t.datetime "created_at", precision: nil, null: false
289 - t.datetime "updated_at", precision: nil, null: false
309 + t.datetime "created_at", precision: nil
310 + t.datetime "updated_at", precision: nil
290 311 t.index ["problem_id"], name: "index_testcases_on_problem_id"
291 312 end
292 313
293 - create_table "user_contest_stats", id: :integer, charset: "utf8", force: :cascade do |t|
314 + create_table "user_contest_stats", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
294 315 t.integer "user_id"
295 316 t.datetime "started_at", precision: nil
296 317 t.datetime "created_at", precision: nil, null: false
@@ -298,7 +319,7
298 319 t.boolean "forced_logout"
299 320 end
300 321
301 - create_table "users", id: :integer, charset: "utf8", force: :cascade do |t|
322 + create_table "users", id: :integer, charset: "utf8mb3", collation: "utf8mb3_unicode_ci", force: :cascade do |t|
302 323 t.string "login", limit: 50
303 324 t.string "full_name"
304 325 t.string "hashed_password"
@@ -310,16 +331,16
310 331 t.boolean "activated", default: false
311 332 t.datetime "created_at", precision: nil
312 333 t.datetime "updated_at", precision: nil
313 - t.string "section"
314 334 t.boolean "enabled", default: true
315 335 t.string "remark"
316 336 t.string "last_ip"
337 + t.string "section"
317 338 t.integer "default_language"
318 339 t.index ["login"], name: "index_users_on_login", unique: true
319 340 end
320 341
342 + add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
343 + add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
321 344 add_foreign_key "problems_tags", "problems"
322 345 add_foreign_key "problems_tags", "tags"
323 - add_foreign_key "solutions", "problems"
324 - add_foreign_key "solutions", "submissions"
325 346 end
deleted file
You need to be logged in to leave comments. Login now