Description:
start update to 5.2
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r751:01b657de42d9 - - 29 files changed: 765 inserted, 140 deleted

@@ -0,0 +1,14
1 + %h1 New announcement
2 + = error_messages_for :announcement
3 + = simple_form_for(@announcement) do |f|
4 + .row
5 + .col-md-6
6 + = f.input :title
7 + = f.input :notes, label: 'Notes (shown internally, used to organize announcements)'
8 + = f.input :body
9 + = f.input :author
10 + = f.input :published
11 + = f.input :frontpage, label: 'Display in the front page only?'
12 + = f.input :contest_only, label: 'Display in contest only?'
13 + = f.button :submit, "Create", class: 'btn btn-primary'
14 + = link_to 'Back', announcements_path, class: 'btn btn-default'
@@ -0,0 +1,24
1 + %p
2 + %b Author:
3 + = h @announcement.author
4 + %p
5 + %b Title:
6 + = h @announcement.title
7 + %p
8 + %b Notes:
9 + = h @announcement.notes
10 + %p
11 + %b Body:
12 + = h markdown(@announcement.body)
13 + %p
14 + %b Published:
15 + = h @announcement.published
16 + %p
17 + %b Show on front page:
18 + = h @announcement.frontpage
19 + %p
20 + %b Show only in contest:
21 + = h @announcement.contest_only
22 + = link_to 'Edit', edit_announcement_path(@announcement)
23 + |
24 + = link_to 'Back', announcements_path
@@ -0,0 +1,179
1 + # frozen_string_literal: true
2 + #
3 + # Uncomment this and change the path if necessary to include your own
4 + # components.
5 + # See https://github.com/plataformatec/simple_form#custom-components to know
6 + # more about custom components.
7 + # Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f }
8 + #
9 + # Use this setup block to configure all options available in SimpleForm.
10 + SimpleForm.setup do |config|
11 + # Wrappers are used by the form builder to generate a
12 + # complete input. You can remove any component from the
13 + # wrapper, change the order or even add your own to the
14 + # stack. The options given below are used to wrap the
15 + # whole input.
16 + config.wrappers :default, class: :input,
17 + hint_class: :field_with_hint, error_class: :field_with_errors, valid_class: :field_without_errors do |b|
18 + ## Extensions enabled by default
19 + # Any of these extensions can be disabled for a
20 + # given input by passing: `f.input EXTENSION_NAME => false`.
21 + # You can make any of these extensions optional by
22 + # renaming `b.use` to `b.optional`.
23 +
24 + # Determines whether to use HTML5 (:email, :url, ...)
25 + # and required attributes
26 + b.use :html5
27 +
28 + # Calculates placeholders automatically from I18n
29 + # You can also pass a string as f.input placeholder: "Placeholder"
30 + b.use :placeholder
31 +
32 + ## Optional extensions
33 + # They are disabled unless you pass `f.input EXTENSION_NAME => true`
34 + # to the input. If so, they will retrieve the values from the model
35 + # if any exists. If you want to enable any of those
36 + # extensions by default, you can change `b.optional` to `b.use`.
37 +
38 + # Calculates maxlength from length validations for string inputs
39 + # and/or database column lengths
40 + b.optional :maxlength
41 +
42 + # Calculate minlength from length validations for string inputs
43 + b.optional :minlength
44 +
45 + # Calculates pattern from format validations for string inputs
46 + b.optional :pattern
47 +
48 + # Calculates min and max from length validations for numeric inputs
49 + b.optional :min_max
50 +
51 + # Calculates readonly automatically from readonly attributes
52 + b.optional :readonly
53 +
54 + ## Inputs
55 + # b.use :input, class: 'input', error_class: 'is-invalid', valid_class: 'is-valid'
56 + b.use :label_input
57 + b.use :hint, wrap_with: { tag: :span, class: :hint }
58 + b.use :error, wrap_with: { tag: :span, class: :error }
59 +
60 + ## full_messages_for
61 + # If you want to display the full error message for the attribute, you can
62 + # use the component :full_error, like:
63 + #
64 + # b.use :full_error, wrap_with: { tag: :span, class: :error }
65 + end
66 +
67 + # The default wrapper to be used by the FormBuilder.
68 + config.default_wrapper = :default
69 +
70 + # Define the way to render check boxes / radio buttons with labels.
71 + # Defaults to :nested for bootstrap config.
72 + # inline: input + label
73 + # nested: label > input
74 + config.boolean_style = :nested
75 +
76 + # Default class for buttons
77 + config.button_class = 'btn'
78 +
79 + # Method used to tidy up errors. Specify any Rails Array method.
80 + # :first lists the first message for each field.
81 + # Use :to_sentence to list all errors for each field.
82 + # config.error_method = :first
83 +
84 + # Default tag used for error notification helper.
85 + config.error_notification_tag = :div
86 +
87 + # CSS class to add for error notification helper.
88 + config.error_notification_class = 'error_notification'
89 +
90 + # Series of attempts to detect a default label method for collection.
91 + # config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
92 +
93 + # Series of attempts to detect a default value method for collection.
94 + # config.collection_value_methods = [ :id, :to_s ]
95 +
96 + # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
97 + # config.collection_wrapper_tag = nil
98 +
99 + # You can define the class to use on all collection wrappers. Defaulting to none.
100 + # config.collection_wrapper_class = nil
101 +
102 + # You can wrap each item in a collection of radio/check boxes with a tag,
103 + # defaulting to :span.
104 + # config.item_wrapper_tag = :span
105 +
106 + # You can define a class to use in all item wrappers. Defaulting to none.
107 + # config.item_wrapper_class = nil
108 +
109 + # How the label text should be generated altogether with the required text.
110 + # config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" }
111 +
112 + # You can define the class to use on all labels. Default is nil.
113 + # config.label_class = nil
114 +
115 + # You can define the default class to be used on forms. Can be overriden
116 + # with `html: { :class }`. Defaulting to none.
117 + # config.default_form_class = nil
118 +
119 + # You can define which elements should obtain additional classes
120 + # config.generate_additional_classes_for = [:wrapper, :label, :input]
121 +
122 + # Whether attributes are required by default (or not). Default is true.
123 + # config.required_by_default = true
124 +
125 + # Tell browsers whether to use the native HTML5 validations (novalidate form option).
126 + # These validations are enabled in SimpleForm's internal config but disabled by default
127 + # in this configuration, which is recommended due to some quirks from different browsers.
128 + # To stop SimpleForm from generating the novalidate option, enabling the HTML5 validations,
129 + # change this configuration to true.
130 + config.browser_validations = false
131 +
132 + # Collection of methods to detect if a file type was given.
133 + # config.file_methods = [ :mounted_as, :file?, :public_filename, :attached? ]
134 +
135 + # Custom mappings for input types. This should be a hash containing a regexp
136 + # to match as key, and the input type that will be used when the field name
137 + # matches the regexp as value.
138 + # config.input_mappings = { /count/ => :integer }
139 +
140 + # Custom wrappers for input types. This should be a hash containing an input
141 + # type as key and the wrapper that will be used for all inputs with specified type.
142 + # config.wrapper_mappings = { string: :prepend }
143 +
144 + # Namespaces where SimpleForm should look for custom input classes that
145 + # override default inputs.
146 + # config.custom_inputs_namespaces << "CustomInputs"
147 +
148 + # Default priority for time_zone inputs.
149 + # config.time_zone_priority = nil
150 +
151 + # Default priority for country inputs.
152 + # config.country_priority = nil
153 +
154 + # When false, do not use translations for labels.
155 + # config.translate_labels = true
156 +
157 + # Automatically discover new inputs in Rails' autoload path.
158 + # config.inputs_discovery = true
159 +
160 + # Cache SimpleForm inputs discovery
161 + # config.cache_discovery = !Rails.env.development?
162 +
163 + # Default class for inputs
164 + # config.input_class = nil
165 +
166 + # Define the default class of the input wrapper of the boolean input.
167 + config.boolean_label_class = 'checkbox'
168 +
169 + # Defines if the default input wrapper class should be included in radio
170 + # collection wrappers.
171 + # config.include_default_input_wrapper_class = true
172 +
173 + # Defines which i18n scope will be used in Simple Form.
174 + # config.i18n_scope = 'simple_form'
175 +
176 + # Defines validation classes to the input_field. By default it's nil.
177 + # config.input_field_valid_class = 'is-valid'
178 + # config.input_field_error_class = 'is-invalid'
179 + end
@@ -0,0 +1,439
1 + # frozen_string_literal: true
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/plataformatec/simple_form/issues/1561
8 +
9 + # Uncomment this and change the path if necessary to include your own
10 + # components.
11 + # See https://github.com/plataformatec/simple_form#custom-components
12 + # to know more about custom components.
13 + # Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f }
14 +
15 + # Use this setup block to configure all options available in SimpleForm.
16 + SimpleForm.setup do |config|
17 + # Default class for buttons
18 + config.button_class = 'btn'
19 +
20 + # Define the default class of the input wrapper of the boolean input.
21 + config.boolean_label_class = 'form-check-label'
22 +
23 + # How the label text should be generated altogether with the required text.
24 + config.label_text = lambda { |label, required, explicit_label| "#{label} #{required}" }
25 +
26 + # Define the way to render check boxes / radio buttons with labels.
27 + config.boolean_style = :inline
28 +
29 + # You can wrap each item in a collection of radio/check boxes with a tag
30 + config.item_wrapper_tag = :div
31 +
32 + # Defines if the default input wrapper class should be included in radio
33 + # collection wrappers.
34 + config.include_default_input_wrapper_class = false
35 +
36 + # CSS class to add for error notification helper.
37 + config.error_notification_class = 'alert alert-danger'
38 +
39 + # Method used to tidy up errors. Specify any Rails Array method.
40 + # :first lists the first message for each field.
41 + # :to_sentence to list all errors for each field.
42 + config.error_method = :to_sentence
43 +
44 + # add validation classes to `input_field`
45 + config.input_field_error_class = 'is-invalid'
46 + config.input_field_valid_class = 'is-valid'
47 +
48 +
49 + # vertical forms
50 + #
51 + # 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|
53 + b.use :html5
54 + b.use :placeholder
55 + b.optional :maxlength
56 + b.optional :minlength
57 + b.optional :pattern
58 + b.optional :min_max
59 + b.optional :readonly
60 + b.use :label, class: 'form-control-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' }
64 + end
65 +
66 + # 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|
68 + b.use :html5
69 + 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'
72 + 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' }
75 + end
76 + end
77 +
78 + # vertical input for radio buttons and check boxes
79 + config.wrappers :vertical_collection, item_wrapper_class: 'form-check', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
80 + b.use :html5
81 + b.optional :readonly
82 + b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
83 + ba.use :label_text
84 + 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' }
88 + end
89 +
90 + # vertical input for inline radio buttons and check boxes
91 + config.wrappers :vertical_collection_inline, item_wrapper_class: 'form-check form-check-inline', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
92 + b.use :html5
93 + b.optional :readonly
94 + b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
95 + ba.use :label_text
96 + 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' }
100 + end
101 +
102 + # 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|
104 + b.use :html5
105 + b.use :placeholder
106 + b.optional :maxlength
107 + b.optional :minlength
108 + 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 d-block' }
112 + b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
113 + end
114 +
115 + # 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|
117 + b.use :html5
118 + b.optional :readonly
119 + b.use :label, class: 'form-control-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'
122 + 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' }
125 + end
126 +
127 + # 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|
129 + b.use :html5
130 + b.use :placeholder
131 + b.optional :readonly
132 + 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' }
137 + end
138 +
139 +
140 + # horizontal forms
141 + #
142 + # 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|
144 + b.use :html5
145 + b.use :placeholder
146 + b.optional :maxlength
147 + b.optional :minlength
148 + b.optional :pattern
149 + b.optional :min_max
150 + b.optional :readonly
151 + 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' }
156 + end
157 + end
158 +
159 + # 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|
161 + b.use :html5
162 + 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'
169 + 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' }
172 + end
173 + end
174 + end
175 +
176 + # horizontal input for radio buttons and check boxes
177 + config.wrappers :horizontal_collection, item_wrapper_class: 'form-check', tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
178 + b.use :html5
179 + b.optional :readonly
180 + b.use :label, class: 'col-sm-3 form-control-label'
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' }
185 + end
186 + end
187 +
188 + # horizontal input for inline radio buttons and check boxes
189 + config.wrappers :horizontal_collection_inline, item_wrapper_class: 'form-check form-check-inline', tag: 'div', class: 'form-group row', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
190 + b.use :html5
191 + b.optional :readonly
192 + b.use :label, class: 'col-sm-3 form-control-label'
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' }
197 + end
198 + end
199 +
200 + # 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|
202 + b.use :html5
203 + b.use :placeholder
204 + b.optional :maxlength
205 + b.optional :minlength
206 + b.optional :readonly
207 + b.use :label, class: 'col-sm-3 form-control-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' }
212 + end
213 + end
214 +
215 + # 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|
217 + b.use :html5
218 + b.optional :readonly
219 + b.use :label, class: 'col-sm-3 control-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'
223 + 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' }
226 + end
227 + end
228 +
229 + # 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|
231 + b.use :html5
232 + b.use :placeholder
233 + b.optional :readonly
234 + b.optional :step
235 + b.use :label, class: 'col-sm-3 form-control-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' }
240 + end
241 + end
242 +
243 +
244 + # inline forms
245 + #
246 + # inline default_wrapper
247 + config.wrappers :inline_form, tag: 'span', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
248 + b.use :html5
249 + b.use :placeholder
250 + b.optional :maxlength
251 + b.optional :minlength
252 + b.optional :pattern
253 + b.optional :min_max
254 + b.optional :readonly
255 + b.use :label, class: 'sr-only'
256 +
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' }
260 + end
261 +
262 + # inline input for boolean
263 + config.wrappers :inline_boolean, tag: 'span', class: 'form-check flex-wrap justify-content-start mr-sm-2', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
264 + b.use :html5
265 + 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' }
270 + end
271 +
272 +
273 + # bootstrap custom forms
274 + #
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 + config.wrappers :custom_boolean_switch, tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
288 + b.use :html5
289 + b.optional :readonly
290 + b.wrapper :form_check_wrapper, tag: 'div', class: 'custom-control custom-checkbox-switch' do |bb|
291 + bb.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
292 + bb.use :label, class: 'custom-control-label'
293 + bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
294 + bb.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
295 + end
296 + end
297 +
298 + # custom input for radio buttons and check boxes
299 + config.wrappers :custom_collection, item_wrapper_class: 'custom-control', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
300 + b.use :html5
301 + b.optional :readonly
302 + b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
303 + ba.use :label_text
304 + end
305 + b.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
306 + b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
307 + b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
308 + end
309 +
310 + # custom input for inline radio buttons and check boxes
311 + config.wrappers :custom_collection_inline, item_wrapper_class: 'custom-control custom-control-inline', tag: 'fieldset', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
312 + b.use :html5
313 + b.optional :readonly
314 + b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
315 + ba.use :label_text
316 + end
317 + b.use :input, class: 'custom-control-input', error_class: 'is-invalid', valid_class: 'is-valid'
318 + b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
319 + b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
320 + end
321 +
322 + # custom file input
323 + config.wrappers :custom_file, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
324 + b.use :html5
325 + b.use :placeholder
326 + b.optional :maxlength
327 + b.optional :minlength
328 + b.optional :readonly
329 + b.use :label, class: 'form-control-label'
330 + b.wrapper :custom_file_wrapper, tag: 'div', class: 'custom-file' do |ba|
331 + ba.use :input, class: 'custom-file-input', error_class: 'is-invalid', valid_class: 'is-valid'
332 + ba.use :label, class: 'custom-file-label'
333 + ba.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
334 + end
335 + b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
336 + end
337 +
338 + # custom multi select
339 + config.wrappers :custom_multi_select, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
340 + b.use :html5
341 + b.optional :readonly
342 + b.use :label, class: 'form-control-label'
343 + b.wrapper tag: 'div', class: 'd-flex flex-row justify-content-between align-items-center' do |ba|
344 + ba.use :input, class: 'custom-select mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
345 + end
346 + b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
347 + b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
348 + end
349 +
350 + # custom range input
351 + config.wrappers :custom_range, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
352 + b.use :html5
353 + b.use :placeholder
354 + b.optional :readonly
355 + b.optional :step
356 + b.use :label, class: 'form-control-label'
357 + b.use :input, class: 'custom-range', error_class: 'is-invalid', valid_class: 'is-valid'
358 + b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
359 + b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
360 + end
361 +
362 +
363 + # Input Group - custom component
364 + # see example app and config at https://github.com/rafaelfranca/simple_form-bootstrap
365 + # config.wrappers :input_group, tag: 'div', class: 'form-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
366 + # b.use :html5
367 + # b.use :placeholder
368 + # b.optional :maxlength
369 + # b.optional :minlength
370 + # b.optional :pattern
371 + # b.optional :min_max
372 + # b.optional :readonly
373 + # b.use :label, class: 'form-control-label'
374 + # b.wrapper :input_group_tag, tag: 'div', class: 'input-group' do |ba|
375 + # ba.optional :prepend
376 + # ba.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
377 + # ba.optional :append
378 + # end
379 + # b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback d-block' }
380 + # b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
381 + # end
382 +
383 +
384 + # Floating Labels form
385 + #
386 + # floating labels default_wrapper
387 + config.wrappers :floating_labels_form, tag: 'div', class: 'form-label-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
388 + b.use :html5
389 + b.use :placeholder
390 + b.optional :maxlength
391 + b.optional :minlength
392 + b.optional :pattern
393 + b.optional :min_max
394 + b.optional :readonly
395 + b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
396 + b.use :label, class: 'form-control-label'
397 + b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
398 + b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
399 + end
400 +
401 + # custom multi select
402 + config.wrappers :floating_labels_select, tag: 'div', class: 'form-label-group', error_class: 'form-group-invalid', valid_class: 'form-group-valid' do |b|
403 + b.use :html5
404 + b.optional :readonly
405 + b.use :input, class: 'custom-select custom-select-lg', error_class: 'is-invalid', valid_class: 'is-valid'
406 + b.use :label, class: 'form-control-label'
407 + b.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
408 + b.use :hint, wrap_with: { tag: 'small', class: 'form-text text-muted' }
409 + end
410 +
411 +
412 + # The default wrapper to be used by the FormBuilder.
413 + config.default_wrapper = :vertical_form
414 +
415 + # Custom wrappers for input types. This should be a hash containing an input
416 + # type as key and the wrapper that will be used for all inputs with specified type.
417 + config.wrapper_mappings = {
418 + boolean: :vertical_boolean,
419 + check_boxes: :vertical_collection,
420 + date: :vertical_multi_select,
421 + datetime: :vertical_multi_select,
422 + file: :vertical_file,
423 + radio_buttons: :vertical_collection,
424 + range: :vertical_range,
425 + time: :vertical_multi_select
426 + }
427 +
428 + # enable custom form wrappers
429 + # config.wrapper_mappings = {
430 + # boolean: :custom_boolean,
431 + # check_boxes: :custom_collection,
432 + # date: :custom_multi_select,
433 + # datetime: :custom_multi_select,
434 + # file: :custom_file,
435 + # radio_buttons: :custom_collection,
436 + # range: :custom_range,
437 + # time: :custom_multi_select
438 + # }
439 + end
@@ -0,0 +1,31
1 + en:
2 + simple_form:
3 + "yes": 'Yes'
4 + "no": 'No'
5 + required:
6 + text: 'required'
7 + mark: '*'
8 + # You can uncomment the line below if you need to overwrite the whole required html.
9 + # When using html, text and mark won't be used.
10 + # html: '<abbr title="required">*</abbr>'
11 + error_notification:
12 + default_message: "Please review the problems below:"
13 + # Examples
14 + # labels:
15 + # defaults:
16 + # password: 'Password'
17 + # user:
18 + # new:
19 + # email: 'E-mail to sign in.'
20 + # edit:
21 + # email: 'E-mail.'
22 + # hints:
23 + # defaults:
24 + # username: 'User name to sign in.'
25 + # password: 'No special characters, please.'
26 + # include_blanks:
27 + # defaults:
28 + # age: 'Rather not say'
29 + # prompts:
30 + # defaults:
31 + # age: 'Select your age'
@@ -0,0 +1,12
1 + -# frozen_string_literal: true
2 + = simple_form_for(@<%= singular_table_name %>) do |f|
3 + = f.error_notification
4 + = f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
5 +
6 + .form-inputs
7 + <%- attributes.each do |attribute| -%>
8 + = f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %>
9 + <%- end -%>
10 +
11 + .form-actions
12 + = f.button :submit
@@ -0,0 +1,5
1 + require "test_helper"
2 +
3 + class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
4 + driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
5 + end
@@ -0,0 +1,37
1 + require "application_system_test_case"
2 +
3 + class AnnouncementsTest < ApplicationSystemTestCase
4 + test "add new announcement" do
5 + visit root_path
6 + fill_in "Login", with: "admin"
7 + fill_in "Password", with: "admin"
8 + click_on "Login"
9 +
10 + assert_text "MAIN"
11 + assert_text "Submission"
12 +
13 + within :css, 'header' do
14 + click_on "Manage"
15 + click_on "Announcements"
16 + end
17 + assert_text "+ Add announcement"
18 +
19 + click_on "Add announcement", match: :first
20 +
21 + fill_in 'Title', with: 'test'
22 + fill_in 'Body', with: 'test body 12345'
23 + check 'Published'
24 +
25 + click_on 'Create'
26 +
27 + visit list_main_path
28 +
29 + assert_text "test body 12345"
30 +
31 + end
32 + # test "visiting the index" do
33 + # visit announcements_url
34 + #
35 + # assert_selector "h1", text: "Announcement"
36 + # end
37 + end
@@ -1,115 +1,116
1 source 'https://rubygems.org'
1 source 'https://rubygems.org'
2 git_source(:github) { |repo| "https://github.com/#{repo}.git" }
2 git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
3
4 #rails
4 #rails
5 gem 'rails', '~>5.2'
5 gem 'rails', '~>5.2'
6 gem 'activerecord-session_store'
6 gem 'activerecord-session_store'
7 gem 'puma'
7 gem 'puma'
8
8
9 # Reduces boot times through caching; required in config/boot.rb
9 # Reduces boot times through caching; required in config/boot.rb
10 gem 'bootsnap', '>= 1.1.0', require: false
10 gem 'bootsnap', '>= 1.1.0', require: false
11
11
12 # Bundle edge Rails instead:
12 # Bundle edge Rails instead:
13 # gem 'rails', :git => 'git://github.com/rails/rails.git'
13 # gem 'rails', :git => 'git://github.com/rails/rails.git'
14
14
15 #---------------- database ---------------------
15 #---------------- database ---------------------
16 #the database
16 #the database
17 gem 'mysql2'
17 gem 'mysql2'
18 #for testing
18 #for testing
19 gem 'sqlite3'
19 gem 'sqlite3'
20 gem 'rails-controller-testing'
20 gem 'rails-controller-testing'
21 #for dumping database into yaml
21 #for dumping database into yaml
22 gem 'yaml_db'
22 gem 'yaml_db'
23
23
24
24
25 #------------- assset pipeline -----------------
25 #------------- assset pipeline -----------------
26 # Gems used only for assets and not required
26 # Gems used only for assets and not required
27 # in production environments by default.
27 # in production environments by default.
28 #sass-rails is depricated
28 #sass-rails is depricated
29 #gem 'sass-rails'
29 #gem 'sass-rails'
30 gem 'sassc-rails'
30 gem 'sassc-rails'
31 gem 'coffee-rails'
31 gem 'coffee-rails'
32
32
33 # See https://github.com/sstephenson/execjs#readme for more supported runtimes
33 # See https://github.com/sstephenson/execjs#readme for more supported runtimes
34 # gem 'therubyracer', :platforms => :ruby
34 # gem 'therubyracer', :platforms => :ruby
35
35
36 gem 'uglifier'
36 gem 'uglifier'
37
37
38 gem 'haml'
38 gem 'haml'
39 gem 'haml-rails'
39 gem 'haml-rails'
40
40
41 # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
41 # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
42 #gem 'turbolinks', '~> 5'
42 #gem 'turbolinks', '~> 5'
43 # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
43 # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
44 gem 'jbuilder', '~> 2.5'
44 gem 'jbuilder', '~> 2.5'
45
45
46
46
47 #in-place editor
47 #in-place editor
48 gem 'best_in_place', '~> 3.0.1'
48 gem 'best_in_place', '~> 3.0.1'
49
49
50 # jquery addition
50 # jquery addition
51 gem 'jquery-rails'
51 gem 'jquery-rails'
52 gem 'jquery-ui-rails'
52 gem 'jquery-ui-rails'
53 gem 'jquery-timepicker-addon-rails'
53 gem 'jquery-timepicker-addon-rails'
54 gem 'jquery-tablesorter'
54 gem 'jquery-tablesorter'
55 gem 'jquery-countdown-rails'
55 gem 'jquery-countdown-rails'
56
56
57 #syntax highlighter
57 #syntax highlighter
58 gem 'rouge'
58 gem 'rouge'
59
59
60 #bootstrap add-ons
60 #bootstrap add-ons
61 gem 'bootstrap-sass', '~> 3.4.1'
61 gem 'bootstrap-sass', '~> 3.4.1'
62 gem 'bootstrap-switch-rails'
62 gem 'bootstrap-switch-rails'
63 gem 'bootstrap-toggle-rails'
63 gem 'bootstrap-toggle-rails'
64 gem 'autoprefixer-rails'
64 gem 'autoprefixer-rails'
65 gem 'momentjs-rails'
65 gem 'momentjs-rails'
66 gem 'rails_bootstrap_sortable'
66 gem 'rails_bootstrap_sortable'
67 gem 'bootstrap-datepicker-rails'
67 gem 'bootstrap-datepicker-rails'
68 gem 'bootstrap3-datetimepicker-rails'
68 gem 'bootstrap3-datetimepicker-rails'
69 gem 'jquery-datatables-rails'
69 gem 'jquery-datatables-rails'
70
70
71 #----------- user interface -----------------
71 #----------- user interface -----------------
72 + gem 'simple_form'
72 #select 2
73 #select 2
73 gem 'select2-rails'
74 gem 'select2-rails'
74 #ace editor
75 #ace editor
75 gem 'ace-rails-ap'
76 gem 'ace-rails-ap'
76 #paginator
77 #paginator
77 gem 'will_paginate', '~> 3.0.7'
78 gem 'will_paginate', '~> 3.0.7'
78
79
79 gem 'mail'
80 gem 'mail'
80 gem 'rdiscount'
81 gem 'rdiscount'
81 gem 'dynamic_form'
82 gem 'dynamic_form'
82 gem 'in_place_editing'
83 gem 'in_place_editing'
83 #gem 'verification', :git => 'https://github.com/sikachu/verification.git'
84 #gem 'verification', :git => 'https://github.com/sikachu/verification.git'
84
85
85
86
86 #---------------- testiing -----------------------
87 #---------------- testiing -----------------------
87 gem 'minitest-reporters'
88 gem 'minitest-reporters'
88
89
89 #---------------- for console --------------------
90 #---------------- for console --------------------
90 gem 'fuzzy-string-match'
91 gem 'fuzzy-string-match'
91
92
92
93
93 group :development, :test do
94 group :development, :test do
94 # Call 'byebug' anywhere in the code to stop execution and get a debugger console
95 # Call 'byebug' anywhere in the code to stop execution and get a debugger console
95 gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
96 gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
96 end
97 end
97
98
98 group :development do
99 group :development do
99 # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
100 # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
100 gem 'web-console', '>= 3.3.0'
101 gem 'web-console', '>= 3.3.0'
101 gem 'listen', '>= 3.0.5', '< 3.2'
102 gem 'listen', '>= 3.0.5', '< 3.2'
102 # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
103 # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
103 gem 'spring'
104 gem 'spring'
104 gem 'spring-watcher-listen', '~> 2.0.0'
105 gem 'spring-watcher-listen', '~> 2.0.0'
105 end
106 end
106
107
107 group :test do
108 group :test do
108 # Adds support for Capybara system testing and selenium driver
109 # Adds support for Capybara system testing and selenium driver
109 gem 'capybara', '>= 2.15'
110 gem 'capybara', '>= 2.15'
110 gem 'selenium-webdriver'
111 gem 'selenium-webdriver'
111 # Easy installation and use of chromedriver to run system tests with Chrome
112 # Easy installation and use of chromedriver to run system tests with Chrome
112 #gem 'chromedriver-helper'
113 #gem 'chromedriver-helper'
113 gem 'webdriver'
114 gem 'webdriver'
114 end
115 end
115
116
@@ -52,282 +52,286
52 i18n (>= 0.7, < 2)
52 i18n (>= 0.7, < 2)
53 minitest (~> 5.1)
53 minitest (~> 5.1)
54 tzinfo (~> 1.1)
54 tzinfo (~> 1.1)
55 addressable (2.6.0)
55 addressable (2.6.0)
56 public_suffix (>= 2.0.2, < 4.0)
56 public_suffix (>= 2.0.2, < 4.0)
57 ansi (1.5.0)
57 ansi (1.5.0)
58 arel (9.0.0)
58 arel (9.0.0)
59 autoprefixer-rails (9.5.1.1)
59 autoprefixer-rails (9.5.1.1)
60 execjs
60 execjs
61 best_in_place (3.0.3)
61 best_in_place (3.0.3)
62 actionpack (>= 3.2)
62 actionpack (>= 3.2)
63 railties (>= 3.2)
63 railties (>= 3.2)
64 bindex (0.7.0)
64 bindex (0.7.0)
65 bootsnap (1.4.4)
65 bootsnap (1.4.4)
66 msgpack (~> 1.0)
66 msgpack (~> 1.0)
67 bootstrap-datepicker-rails (1.8.0.1)
67 bootstrap-datepicker-rails (1.8.0.1)
68 railties (>= 3.0)
68 railties (>= 3.0)
69 bootstrap-sass (3.4.1)
69 bootstrap-sass (3.4.1)
70 autoprefixer-rails (>= 5.2.1)
70 autoprefixer-rails (>= 5.2.1)
71 sassc (>= 2.0.0)
71 sassc (>= 2.0.0)
72 bootstrap-switch-rails (3.3.4)
72 bootstrap-switch-rails (3.3.4)
73 bootstrap-toggle-rails (2.2.1.0)
73 bootstrap-toggle-rails (2.2.1.0)
74 bootstrap3-datetimepicker-rails (4.17.47)
74 bootstrap3-datetimepicker-rails (4.17.47)
75 momentjs-rails (>= 2.8.1)
75 momentjs-rails (>= 2.8.1)
76 builder (3.2.3)
76 builder (3.2.3)
77 byebug (11.0.1)
77 byebug (11.0.1)
78 capybara (3.25.0)
78 capybara (3.25.0)
79 addressable
79 addressable
80 mini_mime (>= 0.1.3)
80 mini_mime (>= 0.1.3)
81 nokogiri (~> 1.8)
81 nokogiri (~> 1.8)
82 rack (>= 1.6.0)
82 rack (>= 1.6.0)
83 rack-test (>= 0.6.3)
83 rack-test (>= 0.6.3)
84 regexp_parser (~> 1.5)
84 regexp_parser (~> 1.5)
85 xpath (~> 3.2)
85 xpath (~> 3.2)
86 childprocess (1.0.1)
86 childprocess (1.0.1)
87 rake (< 13.0)
87 rake (< 13.0)
88 coffee-rails (4.2.2)
88 coffee-rails (4.2.2)
89 coffee-script (>= 2.2.0)
89 coffee-script (>= 2.2.0)
90 railties (>= 4.0.0)
90 railties (>= 4.0.0)
91 coffee-script (2.4.1)
91 coffee-script (2.4.1)
92 coffee-script-source
92 coffee-script-source
93 execjs
93 execjs
94 coffee-script-source (1.12.2)
94 coffee-script-source (1.12.2)
95 concurrent-ruby (1.1.5)
95 concurrent-ruby (1.1.5)
96 crass (1.0.4)
96 crass (1.0.4)
97 dynamic_form (1.1.4)
97 dynamic_form (1.1.4)
98 erubi (1.8.0)
98 erubi (1.8.0)
99 erubis (2.7.0)
99 erubis (2.7.0)
100 execjs (2.7.0)
100 execjs (2.7.0)
101 ffi (1.11.1)
101 ffi (1.11.1)
102 fuzzy-string-match (1.0.1)
102 fuzzy-string-match (1.0.1)
103 RubyInline (>= 3.8.6)
103 RubyInline (>= 3.8.6)
104 globalid (0.4.2)
104 globalid (0.4.2)
105 activesupport (>= 4.2.0)
105 activesupport (>= 4.2.0)
106 haml (5.1.0)
106 haml (5.1.0)
107 temple (>= 0.8.0)
107 temple (>= 0.8.0)
108 tilt
108 tilt
109 haml-rails (1.0.0)
109 haml-rails (1.0.0)
110 actionpack (>= 4.0.1)
110 actionpack (>= 4.0.1)
111 activesupport (>= 4.0.1)
111 activesupport (>= 4.0.1)
112 haml (>= 4.0.6, < 6.0)
112 haml (>= 4.0.6, < 6.0)
113 html2haml (>= 1.0.1)
113 html2haml (>= 1.0.1)
114 railties (>= 4.0.1)
114 railties (>= 4.0.1)
115 html2haml (2.2.0)
115 html2haml (2.2.0)
116 erubis (~> 2.7.0)
116 erubis (~> 2.7.0)
117 haml (>= 4.0, < 6)
117 haml (>= 4.0, < 6)
118 nokogiri (>= 1.6.0)
118 nokogiri (>= 1.6.0)
119 ruby_parser (~> 3.5)
119 ruby_parser (~> 3.5)
120 i18n (1.6.0)
120 i18n (1.6.0)
121 concurrent-ruby (~> 1.0)
121 concurrent-ruby (~> 1.0)
122 in_place_editing (1.2.0)
122 in_place_editing (1.2.0)
123 jbuilder (2.9.1)
123 jbuilder (2.9.1)
124 activesupport (>= 4.2.0)
124 activesupport (>= 4.2.0)
125 jquery-countdown-rails (2.0.2)
125 jquery-countdown-rails (2.0.2)
126 jquery-datatables-rails (3.4.0)
126 jquery-datatables-rails (3.4.0)
127 actionpack (>= 3.1)
127 actionpack (>= 3.1)
128 jquery-rails
128 jquery-rails
129 railties (>= 3.1)
129 railties (>= 3.1)
130 sass-rails
130 sass-rails
131 jquery-rails (4.3.3)
131 jquery-rails (4.3.3)
132 rails-dom-testing (>= 1, < 3)
132 rails-dom-testing (>= 1, < 3)
133 railties (>= 4.2.0)
133 railties (>= 4.2.0)
134 thor (>= 0.14, < 2.0)
134 thor (>= 0.14, < 2.0)
135 jquery-tablesorter (1.26.1)
135 jquery-tablesorter (1.26.1)
136 railties (>= 3.2, < 6)
136 railties (>= 3.2, < 6)
137 jquery-timepicker-addon-rails (1.4.1)
137 jquery-timepicker-addon-rails (1.4.1)
138 railties (>= 3.1)
138 railties (>= 3.1)
139 jquery-ui-rails (6.0.1)
139 jquery-ui-rails (6.0.1)
140 railties (>= 3.2.16)
140 railties (>= 3.2.16)
141 listen (3.1.5)
141 listen (3.1.5)
142 rb-fsevent (~> 0.9, >= 0.9.4)
142 rb-fsevent (~> 0.9, >= 0.9.4)
143 rb-inotify (~> 0.9, >= 0.9.7)
143 rb-inotify (~> 0.9, >= 0.9.7)
144 ruby_dep (~> 1.2)
144 ruby_dep (~> 1.2)
145 loofah (2.2.3)
145 loofah (2.2.3)
146 crass (~> 1.0.2)
146 crass (~> 1.0.2)
147 nokogiri (>= 1.5.9)
147 nokogiri (>= 1.5.9)
148 mail (2.7.1)
148 mail (2.7.1)
149 mini_mime (>= 0.1.1)
149 mini_mime (>= 0.1.1)
150 marcel (0.3.3)
150 marcel (0.3.3)
151 mimemagic (~> 0.3.2)
151 mimemagic (~> 0.3.2)
152 method_source (0.9.2)
152 method_source (0.9.2)
153 mimemagic (0.3.3)
153 mimemagic (0.3.3)
154 mini_mime (1.0.1)
154 mini_mime (1.0.1)
155 mini_portile2 (2.4.0)
155 mini_portile2 (2.4.0)
156 minitest (5.11.3)
156 minitest (5.11.3)
157 minitest-reporters (1.3.6)
157 minitest-reporters (1.3.6)
158 ansi
158 ansi
159 builder
159 builder
160 minitest (>= 5.0)
160 minitest (>= 5.0)
161 ruby-progressbar
161 ruby-progressbar
162 momentjs-rails (2.20.1)
162 momentjs-rails (2.20.1)
163 railties (>= 3.1)
163 railties (>= 3.1)
164 msgpack (1.3.0)
164 msgpack (1.3.0)
165 multi_json (1.13.1)
165 multi_json (1.13.1)
166 mysql2 (0.5.2)
166 mysql2 (0.5.2)
167 nio4r (2.3.1)
167 nio4r (2.3.1)
168 nokogiri (1.10.3)
168 nokogiri (1.10.3)
169 mini_portile2 (~> 2.4.0)
169 mini_portile2 (~> 2.4.0)
170 public_suffix (3.1.1)
170 public_suffix (3.1.1)
171 puma (4.0.0)
171 puma (4.0.0)
172 nio4r (~> 2.0)
172 nio4r (~> 2.0)
173 rack (2.0.7)
173 rack (2.0.7)
174 rack-test (1.1.0)
174 rack-test (1.1.0)
175 rack (>= 1.0, < 3)
175 rack (>= 1.0, < 3)
176 rails (5.2.3)
176 rails (5.2.3)
177 actioncable (= 5.2.3)
177 actioncable (= 5.2.3)
178 actionmailer (= 5.2.3)
178 actionmailer (= 5.2.3)
179 actionpack (= 5.2.3)
179 actionpack (= 5.2.3)
180 actionview (= 5.2.3)
180 actionview (= 5.2.3)
181 activejob (= 5.2.3)
181 activejob (= 5.2.3)
182 activemodel (= 5.2.3)
182 activemodel (= 5.2.3)
183 activerecord (= 5.2.3)
183 activerecord (= 5.2.3)
184 activestorage (= 5.2.3)
184 activestorage (= 5.2.3)
185 activesupport (= 5.2.3)
185 activesupport (= 5.2.3)
186 bundler (>= 1.3.0)
186 bundler (>= 1.3.0)
187 railties (= 5.2.3)
187 railties (= 5.2.3)
188 sprockets-rails (>= 2.0.0)
188 sprockets-rails (>= 2.0.0)
189 rails-controller-testing (1.0.4)
189 rails-controller-testing (1.0.4)
190 actionpack (>= 5.0.1.x)
190 actionpack (>= 5.0.1.x)
191 actionview (>= 5.0.1.x)
191 actionview (>= 5.0.1.x)
192 activesupport (>= 5.0.1.x)
192 activesupport (>= 5.0.1.x)
193 rails-dom-testing (2.0.3)
193 rails-dom-testing (2.0.3)
194 activesupport (>= 4.2.0)
194 activesupport (>= 4.2.0)
195 nokogiri (>= 1.6)
195 nokogiri (>= 1.6)
196 rails-html-sanitizer (1.0.4)
196 rails-html-sanitizer (1.0.4)
197 loofah (~> 2.2, >= 2.2.2)
197 loofah (~> 2.2, >= 2.2.2)
198 rails_bootstrap_sortable (2.0.6)
198 rails_bootstrap_sortable (2.0.6)
199 momentjs-rails (>= 2.8.3)
199 momentjs-rails (>= 2.8.3)
200 railties (5.2.3)
200 railties (5.2.3)
201 actionpack (= 5.2.3)
201 actionpack (= 5.2.3)
202 activesupport (= 5.2.3)
202 activesupport (= 5.2.3)
203 method_source
203 method_source
204 rake (>= 0.8.7)
204 rake (>= 0.8.7)
205 thor (>= 0.19.0, < 2.0)
205 thor (>= 0.19.0, < 2.0)
206 rake (12.3.2)
206 rake (12.3.2)
207 rb-fsevent (0.10.3)
207 rb-fsevent (0.10.3)
208 rb-inotify (0.10.0)
208 rb-inotify (0.10.0)
209 ffi (~> 1.0)
209 ffi (~> 1.0)
210 rdiscount (2.2.0.1)
210 rdiscount (2.2.0.1)
211 regexp_parser (1.5.1)
211 regexp_parser (1.5.1)
212 rouge (3.3.0)
212 rouge (3.3.0)
213 ruby-progressbar (1.10.0)
213 ruby-progressbar (1.10.0)
214 ruby_dep (1.5.0)
214 ruby_dep (1.5.0)
215 ruby_parser (3.13.1)
215 ruby_parser (3.13.1)
216 sexp_processor (~> 4.9)
216 sexp_processor (~> 4.9)
217 rubyzip (1.2.3)
217 rubyzip (1.2.3)
218 sass (3.7.4)
218 sass (3.7.4)
219 sass-listen (~> 4.0.0)
219 sass-listen (~> 4.0.0)
220 sass-listen (4.0.0)
220 sass-listen (4.0.0)
221 rb-fsevent (~> 0.9, >= 0.9.4)
221 rb-fsevent (~> 0.9, >= 0.9.4)
222 rb-inotify (~> 0.9, >= 0.9.7)
222 rb-inotify (~> 0.9, >= 0.9.7)
223 sass-rails (5.0.7)
223 sass-rails (5.0.7)
224 railties (>= 4.0.0, < 6)
224 railties (>= 4.0.0, < 6)
225 sass (~> 3.1)
225 sass (~> 3.1)
226 sprockets (>= 2.8, < 4.0)
226 sprockets (>= 2.8, < 4.0)
227 sprockets-rails (>= 2.0, < 4.0)
227 sprockets-rails (>= 2.0, < 4.0)
228 tilt (>= 1.1, < 3)
228 tilt (>= 1.1, < 3)
229 sassc (2.0.1)
229 sassc (2.0.1)
230 ffi (~> 1.9)
230 ffi (~> 1.9)
231 rake
231 rake
232 sassc-rails (2.1.1)
232 sassc-rails (2.1.1)
233 railties (>= 4.0.0)
233 railties (>= 4.0.0)
234 sassc (>= 2.0)
234 sassc (>= 2.0)
235 sprockets (> 3.0)
235 sprockets (> 3.0)
236 sprockets-rails
236 sprockets-rails
237 tilt
237 tilt
238 select2-rails (4.0.3)
238 select2-rails (4.0.3)
239 thor (~> 0.14)
239 thor (~> 0.14)
240 selenium-webdriver (3.142.3)
240 selenium-webdriver (3.142.3)
241 childprocess (>= 0.5, < 2.0)
241 childprocess (>= 0.5, < 2.0)
242 rubyzip (~> 1.2, >= 1.2.2)
242 rubyzip (~> 1.2, >= 1.2.2)
243 sexp_processor (4.12.0)
243 sexp_processor (4.12.0)
244 + simple_form (4.1.0)
245 + actionpack (>= 5.0)
246 + activemodel (>= 5.0)
244 spring (2.1.0)
247 spring (2.1.0)
245 spring-watcher-listen (2.0.1)
248 spring-watcher-listen (2.0.1)
246 listen (>= 2.7, < 4.0)
249 listen (>= 2.7, < 4.0)
247 spring (>= 1.2, < 3.0)
250 spring (>= 1.2, < 3.0)
248 sprockets (3.7.2)
251 sprockets (3.7.2)
249 concurrent-ruby (~> 1.0)
252 concurrent-ruby (~> 1.0)
250 rack (> 1, < 3)
253 rack (> 1, < 3)
251 sprockets-rails (3.2.1)
254 sprockets-rails (3.2.1)
252 actionpack (>= 4.0)
255 actionpack (>= 4.0)
253 activesupport (>= 4.0)
256 activesupport (>= 4.0)
254 sprockets (>= 3.0.0)
257 sprockets (>= 3.0.0)
255 sqlite3 (1.4.1)
258 sqlite3 (1.4.1)
256 temple (0.8.1)
259 temple (0.8.1)
257 thor (0.20.3)
260 thor (0.20.3)
258 thread_safe (0.3.6)
261 thread_safe (0.3.6)
259 tilt (2.0.9)
262 tilt (2.0.9)
260 tzinfo (1.2.5)
263 tzinfo (1.2.5)
261 thread_safe (~> 0.1)
264 thread_safe (~> 0.1)
262 uglifier (4.1.20)
265 uglifier (4.1.20)
263 execjs (>= 0.3.0, < 3)
266 execjs (>= 0.3.0, < 3)
264 web-console (3.7.0)
267 web-console (3.7.0)
265 actionview (>= 5.0)
268 actionview (>= 5.0)
266 activemodel (>= 5.0)
269 activemodel (>= 5.0)
267 bindex (>= 0.4.0)
270 bindex (>= 0.4.0)
268 railties (>= 5.0)
271 railties (>= 5.0)
269 webdriver (0.1.0)
272 webdriver (0.1.0)
270 websocket-driver (0.7.1)
273 websocket-driver (0.7.1)
271 websocket-extensions (>= 0.1.0)
274 websocket-extensions (>= 0.1.0)
272 websocket-extensions (0.1.4)
275 websocket-extensions (0.1.4)
273 will_paginate (3.0.12)
276 will_paginate (3.0.12)
274 xpath (3.2.0)
277 xpath (3.2.0)
275 nokogiri (~> 1.8)
278 nokogiri (~> 1.8)
276 yaml_db (0.7.0)
279 yaml_db (0.7.0)
277 rails (>= 3.0)
280 rails (>= 3.0)
278 rake (>= 0.8.7)
281 rake (>= 0.8.7)
279
282
280 PLATFORMS
283 PLATFORMS
281 ruby
284 ruby
282
285
283 DEPENDENCIES
286 DEPENDENCIES
284 ace-rails-ap
287 ace-rails-ap
285 activerecord-session_store
288 activerecord-session_store
286 autoprefixer-rails
289 autoprefixer-rails
287 best_in_place (~> 3.0.1)
290 best_in_place (~> 3.0.1)
288 bootsnap (>= 1.1.0)
291 bootsnap (>= 1.1.0)
289 bootstrap-datepicker-rails
292 bootstrap-datepicker-rails
290 bootstrap-sass (~> 3.4.1)
293 bootstrap-sass (~> 3.4.1)
291 bootstrap-switch-rails
294 bootstrap-switch-rails
292 bootstrap-toggle-rails
295 bootstrap-toggle-rails
293 bootstrap3-datetimepicker-rails
296 bootstrap3-datetimepicker-rails
294 byebug
297 byebug
295 capybara (>= 2.15)
298 capybara (>= 2.15)
296 coffee-rails
299 coffee-rails
297 dynamic_form
300 dynamic_form
298 fuzzy-string-match
301 fuzzy-string-match
299 haml
302 haml
300 haml-rails
303 haml-rails
301 in_place_editing
304 in_place_editing
302 jbuilder (~> 2.5)
305 jbuilder (~> 2.5)
303 jquery-countdown-rails
306 jquery-countdown-rails
304 jquery-datatables-rails
307 jquery-datatables-rails
305 jquery-rails
308 jquery-rails
306 jquery-tablesorter
309 jquery-tablesorter
307 jquery-timepicker-addon-rails
310 jquery-timepicker-addon-rails
308 jquery-ui-rails
311 jquery-ui-rails
309 listen (>= 3.0.5, < 3.2)
312 listen (>= 3.0.5, < 3.2)
310 mail
313 mail
311 minitest-reporters
314 minitest-reporters
312 momentjs-rails
315 momentjs-rails
313 mysql2
316 mysql2
314 puma
317 puma
315 rails (~> 5.2)
318 rails (~> 5.2)
316 rails-controller-testing
319 rails-controller-testing
317 rails_bootstrap_sortable
320 rails_bootstrap_sortable
318 rdiscount
321 rdiscount
319 rouge
322 rouge
320 sassc-rails
323 sassc-rails
321 select2-rails
324 select2-rails
322 selenium-webdriver
325 selenium-webdriver
326 + simple_form
323 spring
327 spring
324 spring-watcher-listen (~> 2.0.0)
328 spring-watcher-listen (~> 2.0.0)
325 sqlite3
329 sqlite3
326 uglifier
330 uglifier
327 web-console (>= 3.3.0)
331 web-console (>= 3.3.0)
328 webdriver
332 webdriver
329 will_paginate (~> 3.0.7)
333 will_paginate (~> 3.0.7)
330 yaml_db
334 yaml_db
331
335
332 BUNDLED WITH
336 BUNDLED WITH
333 1.17.2
337 1.17.2
@@ -1,93 +1,86
1 class GradersController < ApplicationController
1 class GradersController < ApplicationController
2
2
3 before_action :admin_authorization
3 before_action :admin_authorization
4
4
5 - verify :method => :post, :only => ['clear_all',
6 - 'start_exam',
7 - 'start_grading',
8 - 'stop_all',
9 - 'clear_terminated'],
10 - :redirect_to => {:action => 'index'}
11 -
12 def index
5 def index
13 redirect_to :action => 'list'
6 redirect_to :action => 'list'
14 end
7 end
15
8
16 def list
9 def list
17 @grader_processes = GraderProcess.find_running_graders
10 @grader_processes = GraderProcess.find_running_graders
18 @stalled_processes = GraderProcess.find_stalled_process
11 @stalled_processes = GraderProcess.find_stalled_process
19
12
20 @terminated_processes = GraderProcess.find_terminated_graders
13 @terminated_processes = GraderProcess.find_terminated_graders
21
14
22 @last_task = Task.last
15 @last_task = Task.last
23 @last_test_request = TestRequest.last
16 @last_test_request = TestRequest.last
24 @submission = Submission.order("id desc").limit(20)
17 @submission = Submission.order("id desc").limit(20)
25 @backlog_submission = Submission.where('graded_at is null')
18 @backlog_submission = Submission.where('graded_at is null')
26 end
19 end
27
20
28 def clear
21 def clear
29 grader_proc = GraderProcess.find(params[:id])
22 grader_proc = GraderProcess.find(params[:id])
30 grader_proc.destroy if grader_proc!=nil
23 grader_proc.destroy if grader_proc!=nil
31 redirect_to :action => 'list'
24 redirect_to :action => 'list'
32 end
25 end
33
26
34 def clear_terminated
27 def clear_terminated
35 GraderProcess.find_terminated_graders.each do |p|
28 GraderProcess.find_terminated_graders.each do |p|
36 p.destroy
29 p.destroy
37 end
30 end
38 redirect_to :action => 'list'
31 redirect_to :action => 'list'
39 end
32 end
40
33
41 def clear_all
34 def clear_all
42 GraderProcess.all.each do |p|
35 GraderProcess.all.each do |p|
43 p.destroy
36 p.destroy
44 end
37 end
45 redirect_to :action => 'list'
38 redirect_to :action => 'list'
46 end
39 end
47
40
48 def view
41 def view
49 if params[:type]=='Task'
42 if params[:type]=='Task'
50 redirect_to :action => 'task', :id => params[:id]
43 redirect_to :action => 'task', :id => params[:id]
51 else
44 else
52 redirect_to :action => 'test_request', :id => params[:id]
45 redirect_to :action => 'test_request', :id => params[:id]
53 end
46 end
54 end
47 end
55
48
56 def test_request
49 def test_request
57 @test_request = TestRequest.find(params[:id])
50 @test_request = TestRequest.find(params[:id])
58 end
51 end
59
52
60 def task
53 def task
61 @task = Task.find(params[:id])
54 @task = Task.find(params[:id])
62 end
55 end
63
56
64
57
65 # various grader controls
58 # various grader controls
66
59
67 def stop
60 def stop
68 grader_proc = GraderProcess.find(params[:id])
61 grader_proc = GraderProcess.find(params[:id])
69 GraderScript.stop_grader(grader_proc.pid)
62 GraderScript.stop_grader(grader_proc.pid)
70 flash[:notice] = 'Grader stopped. It may not disappear now, but it should disappear shortly.'
63 flash[:notice] = 'Grader stopped. It may not disappear now, but it should disappear shortly.'
71 redirect_to :action => 'list'
64 redirect_to :action => 'list'
72 end
65 end
73
66
74 def stop_all
67 def stop_all
75 GraderScript.stop_graders(GraderProcess.find_running_graders +
68 GraderScript.stop_graders(GraderProcess.find_running_graders +
76 GraderProcess.find_stalled_process)
69 GraderProcess.find_stalled_process)
77 flash[:notice] = 'Graders stopped. They may not disappear now, but they should disappear shortly.'
70 flash[:notice] = 'Graders stopped. They may not disappear now, but they should disappear shortly.'
78 redirect_to :action => 'list'
71 redirect_to :action => 'list'
79 end
72 end
80
73
81 def start_grading
74 def start_grading
82 GraderScript.start_grader('grading')
75 GraderScript.start_grader('grading')
83 flash[:notice] = '2 graders in grading env started, one for grading queue tasks, another for grading test request'
76 flash[:notice] = '2 graders in grading env started, one for grading queue tasks, another for grading test request'
84 redirect_to :action => 'list'
77 redirect_to :action => 'list'
85 end
78 end
86
79
87 def start_exam
80 def start_exam
88 GraderScript.start_grader('exam')
81 GraderScript.start_grader('exam')
89 flash[:notice] = '2 graders in grading env started, one for grading queue tasks, another for grading test request'
82 flash[:notice] = '2 graders in grading env started, one for grading queue tasks, another for grading test request'
90 redirect_to :action => 'list'
83 redirect_to :action => 'list'
91 end
84 end
92
85
93 end
86 end
@@ -1,218 +1,209
1 class MainController < ApplicationController
1 class MainController < ApplicationController
2
2
3 before_action :authenticate, :except => [:index, :login]
3 before_action :authenticate, :except => [:index, :login]
4 before_action :check_viewability, :except => [:index, :login]
4 before_action :check_viewability, :except => [:index, :login]
5
5
6 append_before_action :confirm_and_update_start_time,
6 append_before_action :confirm_and_update_start_time,
7 :except => [:index,
7 :except => [:index,
8 :login,
8 :login,
9 :confirm_contest_start]
9 :confirm_contest_start]
10
10
11 # to prevent log in box to be shown when user logged out of the
11 # to prevent log in box to be shown when user logged out of the
12 # system only in some tab
12 # system only in some tab
13 prepend_before_action :reject_announcement_refresh_when_logged_out,
13 prepend_before_action :reject_announcement_refresh_when_logged_out,
14 :only => [:announcements]
14 :only => [:announcements]
15
15
16 before_action :authenticate_by_ip_address, :only => [:list]
16 before_action :authenticate_by_ip_address, :only => [:list]
17
17
18 - # COMMENTED OUT: filter in each action instead
19 - # before_filter :verify_time_limit, :only => [:submit]
20 -
21 - verify :method => :post, :only => [:submit],
22 - :redirect_to => { :action => :index }
23 -
24 - # COMMENT OUT: only need when having high load
25 - # caches_action :index, :login
26 -
27 # NOTE: This method is not actually needed, 'config/routes.rb' has
18 # NOTE: This method is not actually needed, 'config/routes.rb' has
28 # assigned action login as a default action.
19 # assigned action login as a default action.
29 def index
20 def index
30 redirect_to :action => 'login'
21 redirect_to :action => 'login'
31 end
22 end
32
23
33 def login
24 def login
34 saved_notice = flash[:notice]
25 saved_notice = flash[:notice]
35 reset_session
26 reset_session
36 flash.now[:notice] = saved_notice
27 flash.now[:notice] = saved_notice
37
28
38 # EXPERIMENT:
29 # EXPERIMENT:
39 # Hide login if in single user mode and the url does not
30 # Hide login if in single user mode and the url does not
40 # explicitly specify /login
31 # explicitly specify /login
41 #
32 #
42 # logger.info "PATH: #{request.path}"
33 # logger.info "PATH: #{request.path}"
43 # if GraderConfiguration['system.single_user_mode'] and
34 # if GraderConfiguration['system.single_user_mode'] and
44 # request.path!='/main/login'
35 # request.path!='/main/login'
45 # @hidelogin = true
36 # @hidelogin = true
46 # end
37 # end
47
38
48 @announcements = Announcement.frontpage
39 @announcements = Announcement.frontpage
49 render :action => 'login', :layout => 'empty'
40 render :action => 'login', :layout => 'empty'
50 end
41 end
51
42
52 def list
43 def list
53 prepare_list_information
44 prepare_list_information
54 end
45 end
55
46
56 def help
47 def help
57 @user = User.find(session[:user_id])
48 @user = User.find(session[:user_id])
58 end
49 end
59
50
60 def submit
51 def submit
61 user = User.find(session[:user_id])
52 user = User.find(session[:user_id])
62
53
63 @submission = Submission.new
54 @submission = Submission.new
64 @submission.problem_id = params[:submission][:problem_id]
55 @submission.problem_id = params[:submission][:problem_id]
65 @submission.user = user
56 @submission.user = user
66 @submission.language_id = 0
57 @submission.language_id = 0
67 if (params['file']) and (params['file']!='')
58 if (params['file']) and (params['file']!='')
68 @submission.source = File.open(params['file'].path,'r:UTF-8',&:read)
59 @submission.source = File.open(params['file'].path,'r:UTF-8',&:read)
69 @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '')
60 @submission.source.encode!('UTF-8','UTF-8',invalid: :replace, replace: '')
70 @submission.source_filename = params['file'].original_filename
61 @submission.source_filename = params['file'].original_filename
71 end
62 end
72
63
73 if (params[:editor_text])
64 if (params[:editor_text])
74 language = Language.find_by_id(params[:language_id])
65 language = Language.find_by_id(params[:language_id])
75 @submission.source = params[:editor_text]
66 @submission.source = params[:editor_text]
76 @submission.source_filename = "live_edit.#{language.ext}"
67 @submission.source_filename = "live_edit.#{language.ext}"
77 @submission.language = language
68 @submission.language = language
78 end
69 end
79
70
80 @submission.submitted_at = Time.new.gmtime
71 @submission.submitted_at = Time.new.gmtime
81 @submission.ip_address = request.remote_ip
72 @submission.ip_address = request.remote_ip
82
73
83 if GraderConfiguration.time_limit_mode? and user.contest_finished?
74 if GraderConfiguration.time_limit_mode? and user.contest_finished?
84 @submission.errors.add(:base,"The contest is over.")
75 @submission.errors.add(:base,"The contest is over.")
85 prepare_list_information
76 prepare_list_information
86 render :action => 'list' and return
77 render :action => 'list' and return
87 end
78 end
88
79
89 if @submission.valid?(@current_user)
80 if @submission.valid?(@current_user)
90 if @submission.save == false
81 if @submission.save == false
91 flash[:notice] = 'Error saving your submission'
82 flash[:notice] = 'Error saving your submission'
92 elsif Task.create(:submission_id => @submission.id,
83 elsif Task.create(:submission_id => @submission.id,
93 :status => Task::STATUS_INQUEUE) == false
84 :status => Task::STATUS_INQUEUE) == false
94 flash[:notice] = 'Error adding your submission to task queue'
85 flash[:notice] = 'Error adding your submission to task queue'
95 end
86 end
96 else
87 else
97 prepare_list_information
88 prepare_list_information
98 render :action => 'list' and return
89 render :action => 'list' and return
99 end
90 end
100 redirect_to edit_submission_path(@submission)
91 redirect_to edit_submission_path(@submission)
101 end
92 end
102
93
103 def source
94 def source
104 submission = Submission.find(params[:id])
95 submission = Submission.find(params[:id])
105 if ((submission.user_id == session[:user_id]) and
96 if ((submission.user_id == session[:user_id]) and
106 (submission.problem != nil) and
97 (submission.problem != nil) and
107 (submission.problem.available))
98 (submission.problem.available))
108 send_data(submission.source,
99 send_data(submission.source,
109 {:filename => submission.download_filename,
100 {:filename => submission.download_filename,
110 :type => 'text/plain'})
101 :type => 'text/plain'})
111 else
102 else
112 flash[:notice] = 'Error viewing source'
103 flash[:notice] = 'Error viewing source'
113 redirect_to :action => 'list'
104 redirect_to :action => 'list'
114 end
105 end
115 end
106 end
116
107
117 def compiler_msg
108 def compiler_msg
118 @submission = Submission.find(params[:id])
109 @submission = Submission.find(params[:id])
119 if @submission.user_id == session[:user_id]
110 if @submission.user_id == session[:user_id]
120 render :action => 'compiler_msg', :layout => 'empty'
111 render :action => 'compiler_msg', :layout => 'empty'
121 else
112 else
122 flash[:notice] = 'Error viewing source'
113 flash[:notice] = 'Error viewing source'
123 redirect_to :action => 'list'
114 redirect_to :action => 'list'
124 end
115 end
125 end
116 end
126
117
127 def result
118 def result
128 if !GraderConfiguration.show_grading_result
119 if !GraderConfiguration.show_grading_result
129 redirect_to :action => 'list' and return
120 redirect_to :action => 'list' and return
130 end
121 end
131 @user = User.find(session[:user_id])
122 @user = User.find(session[:user_id])
132 @submission = Submission.find(params[:id])
123 @submission = Submission.find(params[:id])
133 if @submission.user!=@user
124 if @submission.user!=@user
134 flash[:notice] = 'You are not allowed to view result of other users.'
125 flash[:notice] = 'You are not allowed to view result of other users.'
135 redirect_to :action => 'list' and return
126 redirect_to :action => 'list' and return
136 end
127 end
137 prepare_grading_result(@submission)
128 prepare_grading_result(@submission)
138 end
129 end
139
130
140 def load_output
131 def load_output
141 if !GraderConfiguration.show_grading_result or params[:num]==nil
132 if !GraderConfiguration.show_grading_result or params[:num]==nil
142 redirect_to :action => 'list' and return
133 redirect_to :action => 'list' and return
143 end
134 end
144 @user = User.find(session[:user_id])
135 @user = User.find(session[:user_id])
145 @submission = Submission.find(params[:id])
136 @submission = Submission.find(params[:id])
146 if @submission.user!=@user
137 if @submission.user!=@user
147 flash[:notice] = 'You are not allowed to view result of other users.'
138 flash[:notice] = 'You are not allowed to view result of other users.'
148 redirect_to :action => 'list' and return
139 redirect_to :action => 'list' and return
149 end
140 end
150 case_num = params[:num].to_i
141 case_num = params[:num].to_i
151 out_filename = output_filename(@user.login,
142 out_filename = output_filename(@user.login,
152 @submission.problem.name,
143 @submission.problem.name,
153 @submission.id,
144 @submission.id,
154 case_num)
145 case_num)
155 if !FileTest.exists?(out_filename)
146 if !FileTest.exists?(out_filename)
156 flash[:notice] = 'Output not found.'
147 flash[:notice] = 'Output not found.'
157 redirect_to :action => 'list' and return
148 redirect_to :action => 'list' and return
158 end
149 end
159
150
160 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
151 if defined?(USE_APACHE_XSENDFILE) and USE_APACHE_XSENDFILE
161 response.headers['Content-Type'] = "application/force-download"
152 response.headers['Content-Type'] = "application/force-download"
162 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
153 response.headers['Content-Disposition'] = "attachment; filename=\"output-#{case_num}.txt\""
163 response.headers["X-Sendfile"] = out_filename
154 response.headers["X-Sendfile"] = out_filename
164 response.headers['Content-length'] = File.size(out_filename)
155 response.headers['Content-length'] = File.size(out_filename)
165 render :nothing => true
156 render :nothing => true
166 else
157 else
167 send_file out_filename, :stream => false, :filename => "output-#{case_num}.txt", :type => "text/plain"
158 send_file out_filename, :stream => false, :filename => "output-#{case_num}.txt", :type => "text/plain"
168 end
159 end
169 end
160 end
170
161
171 def error
162 def error
172 @user = User.find(session[:user_id])
163 @user = User.find(session[:user_id])
173 end
164 end
174
165
175 # announcement refreshing and hiding methods
166 # announcement refreshing and hiding methods
176
167
177 def announcements
168 def announcements
178 if params.has_key? 'recent'
169 if params.has_key? 'recent'
179 prepare_announcements(params[:recent])
170 prepare_announcements(params[:recent])
180 else
171 else
181 prepare_announcements
172 prepare_announcements
182 end
173 end
183 render(:partial => 'announcement',
174 render(:partial => 'announcement',
184 :collection => @announcements,
175 :collection => @announcements,
185 :locals => {:announcement_effect => true})
176 :locals => {:announcement_effect => true})
186 end
177 end
187
178
188 def confirm_contest_start
179 def confirm_contest_start
189 user = User.find(session[:user_id])
180 user = User.find(session[:user_id])
190 if request.method == 'POST'
181 if request.method == 'POST'
191 user.update_start_time
182 user.update_start_time
192 redirect_to :action => 'list'
183 redirect_to :action => 'list'
193 else
184 else
194 @contests = user.contests
185 @contests = user.contests
195 @user = user
186 @user = user
196 end
187 end
197 end
188 end
198
189
199 protected
190 protected
200
191
201 def prepare_announcements(recent=nil)
192 def prepare_announcements(recent=nil)
202 if GraderConfiguration.show_tasks_to?(@user)
193 if GraderConfiguration.show_tasks_to?(@user)
203 @announcements = Announcement.published(true)
194 @announcements = Announcement.published(true)
204 else
195 else
205 @announcements = Announcement.published
196 @announcements = Announcement.published
206 end
197 end
207 if recent!=nil
198 if recent!=nil
208 recent_id = recent.to_i
199 recent_id = recent.to_i
209 @announcements = @announcements.find_all { |a| a.id > recent_id }
200 @announcements = @announcements.find_all { |a| a.id > recent_id }
210 end
201 end
211 end
202 end
212
203
213 def prepare_list_information
204 def prepare_list_information
214 @user = User.find(session[:user_id])
205 @user = User.find(session[:user_id])
215 if not GraderConfiguration.multicontests?
206 if not GraderConfiguration.multicontests?
216 @problems = @user.available_problems
207 @problems = @user.available_problems
217 else
208 else
218 @contest_problems = @user.available_problems_group_by_contests
209 @contest_problems = @user.available_problems_group_by_contests
@@ -1,86 +1,84
1 class MessagesController < ApplicationController
1 class MessagesController < ApplicationController
2
2
3 before_action :authenticate
3 before_action :authenticate
4
4
5 - verify :method => :post, :only => ['create'],
6 - :redirect_to => { :action => 'list' }
7
5
8 before_filter :admin_authorization, :only => ['console','show',
6 before_filter :admin_authorization, :only => ['console','show',
9 'reply','hide','list_all']
7 'reply','hide','list_all']
10
8
11 def list
9 def list
12 @user = User.find(session[:user_id])
10 @user = User.find(session[:user_id])
13 @messages = Message.find_all_sent_by_user(@user)
11 @messages = Message.find_all_sent_by_user(@user)
14 end
12 end
15
13
16 def console
14 def console
17 @user = User.find(session[:user_id])
15 @user = User.find(session[:user_id])
18 @messages = Message.find_all_system_unreplied_messages
16 @messages = Message.find_all_system_unreplied_messages
19 end
17 end
20
18
21 def show
19 def show
22 @message = Message.find(params[:id])
20 @message = Message.find(params[:id])
23 end
21 end
24
22
25 def list_all
23 def list_all
26 @user = User.find(session[:user_id])
24 @user = User.find(session[:user_id])
27 @messages = Message.where(receiver_id: nil).order(:created_at)
25 @messages = Message.where(receiver_id: nil).order(:created_at)
28 end
26 end
29
27
30 def create
28 def create
31 user = User.find(session[:user_id])
29 user = User.find(session[:user_id])
32 @message = Message.new(params[:message])
30 @message = Message.new(params[:message])
33 @message.sender = user
31 @message.sender = user
34 if @message.body == '' or !@message.save
32 if @message.body == '' or !@message.save
35 flash[:notice] = 'An error occurred'
33 flash[:notice] = 'An error occurred'
36 else
34 else
37 flash[:notice] = 'New message posted'
35 flash[:notice] = 'New message posted'
38 end
36 end
39 redirect_to :action => 'list'
37 redirect_to :action => 'list'
40 end
38 end
41
39
42 def reply
40 def reply
43 user = User.find(session[:user_id])
41 user = User.find(session[:user_id])
44 @message = Message.new(params[:r_message])
42 @message = Message.new(params[:r_message])
45 @message.sender = user
43 @message.sender = user
46 if @message.body == '' or !@message.save
44 if @message.body == '' or !@message.save
47 flash[:notice] = 'An error occurred'
45 flash[:notice] = 'An error occurred'
48 redirect_to :action => 'show', :id => @message.replying_message_id
46 redirect_to :action => 'show', :id => @message.replying_message_id
49 else
47 else
50 flash[:notice] = 'Message replied'
48 flash[:notice] = 'Message replied'
51 rep_msg = @message.replying_message
49 rep_msg = @message.replying_message
52 rep_msg.replied = true
50 rep_msg.replied = true
53 rep_msg.save
51 rep_msg.save
54 redirect_to :action => 'console'
52 redirect_to :action => 'console'
55 end
53 end
56 end
54 end
57
55
58 def hide
56 def hide
59 message = Message.find(params[:id])
57 message = Message.find(params[:id])
60 message.replied = true
58 message.replied = true
61 message.save
59 message.save
62 flash[:notice] = 'Message hidden (just marked replied)'
60 flash[:notice] = 'Message hidden (just marked replied)'
63 redirect_to :action => 'console'
61 redirect_to :action => 'console'
64 end
62 end
65
63
66 protected
64 protected
67 def build_replying_message_hierarchy(user)
65 def build_replying_message_hierarchy(user)
68 @all_messages = {}
66 @all_messages = {}
69
67
70
68
71 # manually build replies hierarchy (to improve efficiency)
69 # manually build replies hierarchy (to improve efficiency)
72 [@messages, @replied_messages].each do |collection|
70 [@messages, @replied_messages].each do |collection|
73 collection.each do |m|
71 collection.each do |m|
74 @all_messages[m.id] = {:msg => m, :replies => []}
72 @all_messages[m.id] = {:msg => m, :replies => []}
75 end
73 end
76 end
74 end
77
75
78 @all_messages.each do |m|
76 @all_messages.each do |m|
79 rep_id = m.replying_message_id
77 rep_id = m.replying_message_id
80 if @all_messages[rep_id]!=nil
78 if @all_messages[rep_id]!=nil
81 @all_messages[rep_id][:replies] << m
79 @all_messages[rep_id][:replies] << m
82 end
80 end
83 end
81 end
84 end
82 end
85
83
86 end
84 end
@@ -1,224 +1,218
1 class ProblemsController < ApplicationController
1 class ProblemsController < ApplicationController
2
2
3 before_action :authenticate, :authorization
3 before_action :authenticate, :authorization
4 before_action :testcase_authorization, only: [:show_testcase]
4 before_action :testcase_authorization, only: [:show_testcase]
5
5
6 in_place_edit_for :problem, :name
6 in_place_edit_for :problem, :name
7 in_place_edit_for :problem, :full_name
7 in_place_edit_for :problem, :full_name
8 in_place_edit_for :problem, :full_score
8 in_place_edit_for :problem, :full_score
9
9
10 def index
10 def index
11 @problems = Problem.order(date_added: :desc)
11 @problems = Problem.order(date_added: :desc)
12 end
12 end
13
13
14 - # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
15 - verify :method => :post, :only => [ :create, :quick_create,
16 - :do_manage,
17 - :do_import,
18 - ],
19 - :redirect_to => { :action => :index }
20
14
21 def show
15 def show
22 @problem = Problem.find(params[:id])
16 @problem = Problem.find(params[:id])
23 end
17 end
24
18
25 def new
19 def new
26 @problem = Problem.new
20 @problem = Problem.new
27 @description = nil
21 @description = nil
28 end
22 end
29
23
30 def create
24 def create
31 @problem = Problem.new(problem_params)
25 @problem = Problem.new(problem_params)
32 - @description = Description.new(params[:description])
26 + @description = Description.new(problem_params[:description])
33 if @description.body!=''
27 if @description.body!=''
34 if !@description.save
28 if !@description.save
35 render :action => new and return
29 render :action => new and return
36 end
30 end
37 else
31 else
38 @description = nil
32 @description = nil
39 end
33 end
40 @problem.description = @description
34 @problem.description = @description
41 if @problem.save
35 if @problem.save
42 flash[:notice] = 'Problem was successfully created.'
36 flash[:notice] = 'Problem was successfully created.'
43 redirect_to action: :index
37 redirect_to action: :index
44 else
38 else
45 render :action => 'new'
39 render :action => 'new'
46 end
40 end
47 end
41 end
48
42
49 def quick_create
43 def quick_create
50 @problem = Problem.new(problem_params)
44 @problem = Problem.new(problem_params)
51 @problem.full_name = @problem.name if @problem.full_name == ''
45 @problem.full_name = @problem.name if @problem.full_name == ''
52 @problem.full_score = 100
46 @problem.full_score = 100
53 @problem.available = false
47 @problem.available = false
54 @problem.test_allowed = true
48 @problem.test_allowed = true
55 @problem.output_only = false
49 @problem.output_only = false
56 @problem.date_added = Time.new
50 @problem.date_added = Time.new
57 if @problem.save
51 if @problem.save
58 flash[:notice] = 'Problem was successfully created.'
52 flash[:notice] = 'Problem was successfully created.'
59 redirect_to action: :index
53 redirect_to action: :index
60 else
54 else
61 flash[:notice] = 'Error saving problem'
55 flash[:notice] = 'Error saving problem'
62 redirect_to action: :index
56 redirect_to action: :index
63 end
57 end
64 end
58 end
65
59
66 def edit
60 def edit
67 @problem = Problem.find(params[:id])
61 @problem = Problem.find(params[:id])
68 @description = @problem.description
62 @description = @problem.description
69 end
63 end
70
64
71 def update
65 def update
72 @problem = Problem.find(params[:id])
66 @problem = Problem.find(params[:id])
73 @description = @problem.description
67 @description = @problem.description
74 if @description.nil? and params[:description][:body]!=''
68 if @description.nil? and params[:description][:body]!=''
75 @description = Description.new(params[:description])
69 @description = Description.new(params[:description])
76 if !@description.save
70 if !@description.save
77 flash[:notice] = 'Error saving description'
71 flash[:notice] = 'Error saving description'
78 render :action => 'edit' and return
72 render :action => 'edit' and return
79 end
73 end
80 @problem.description = @description
74 @problem.description = @description
81 elsif @description
75 elsif @description
82 if !@description.update_attributes(params[:description])
76 if !@description.update_attributes(params[:description])
83 flash[:notice] = 'Error saving description'
77 flash[:notice] = 'Error saving description'
84 render :action => 'edit' and return
78 render :action => 'edit' and return
85 end
79 end
86 end
80 end
87 if params[:file] and params[:file].content_type != 'application/pdf'
81 if params[:file] and params[:file].content_type != 'application/pdf'
88 flash[:notice] = 'Error: Uploaded file is not PDF'
82 flash[:notice] = 'Error: Uploaded file is not PDF'
89 render :action => 'edit' and return
83 render :action => 'edit' and return
90 end
84 end
91 if @problem.update_attributes(problem_params)
85 if @problem.update_attributes(problem_params)
92 flash[:notice] = 'Problem was successfully updated.'
86 flash[:notice] = 'Problem was successfully updated.'
93 unless params[:file] == nil or params[:file] == ''
87 unless params[:file] == nil or params[:file] == ''
94 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
88 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
95 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
89 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
96 if not FileTest.exists? out_dirname
90 if not FileTest.exists? out_dirname
97 Dir.mkdir out_dirname
91 Dir.mkdir out_dirname
98 end
92 end
99
93
100 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
94 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
101 if FileTest.exists? out_filename
95 if FileTest.exists? out_filename
102 File.delete out_filename
96 File.delete out_filename
103 end
97 end
104
98
105 File.open(out_filename,"wb") do |file|
99 File.open(out_filename,"wb") do |file|
106 file.write(params[:file].read)
100 file.write(params[:file].read)
107 end
101 end
108 @problem.description_filename = "#{@problem.name}.pdf"
102 @problem.description_filename = "#{@problem.name}.pdf"
109 @problem.save
103 @problem.save
110 end
104 end
111 redirect_to :action => 'show', :id => @problem
105 redirect_to :action => 'show', :id => @problem
112 else
106 else
113 render :action => 'edit'
107 render :action => 'edit'
114 end
108 end
115 end
109 end
116
110
117 def destroy
111 def destroy
118 p = Problem.find(params[:id]).destroy
112 p = Problem.find(params[:id]).destroy
119 redirect_to action: :index
113 redirect_to action: :index
120 end
114 end
121
115
122 def toggle
116 def toggle
123 @problem = Problem.find(params[:id])
117 @problem = Problem.find(params[:id])
124 @problem.update_attributes(available: !(@problem.available) )
118 @problem.update_attributes(available: !(@problem.available) )
125 respond_to do |format|
119 respond_to do |format|
126 format.js { }
120 format.js { }
127 end
121 end
128 end
122 end
129
123
130 def toggle_test
124 def toggle_test
131 @problem = Problem.find(params[:id])
125 @problem = Problem.find(params[:id])
132 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
126 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
133 respond_to do |format|
127 respond_to do |format|
134 format.js { }
128 format.js { }
135 end
129 end
136 end
130 end
137
131
138 def toggle_view_testcase
132 def toggle_view_testcase
139 @problem = Problem.find(params[:id])
133 @problem = Problem.find(params[:id])
140 @problem.update_attributes(view_testcase: !(@problem.view_testcase?) )
134 @problem.update_attributes(view_testcase: !(@problem.view_testcase?) )
141 respond_to do |format|
135 respond_to do |format|
142 format.js { }
136 format.js { }
143 end
137 end
144 end
138 end
145
139
146 def turn_all_off
140 def turn_all_off
147 Problem.available.all.each do |problem|
141 Problem.available.all.each do |problem|
148 problem.available = false
142 problem.available = false
149 problem.save
143 problem.save
150 end
144 end
151 redirect_to action: :index
145 redirect_to action: :index
152 end
146 end
153
147
154 def turn_all_on
148 def turn_all_on
155 Problem.where.not(available: true).each do |problem|
149 Problem.where.not(available: true).each do |problem|
156 problem.available = true
150 problem.available = true
157 problem.save
151 problem.save
158 end
152 end
159 redirect_to action: :index
153 redirect_to action: :index
160 end
154 end
161
155
162 def stat
156 def stat
163 @problem = Problem.find(params[:id])
157 @problem = Problem.find(params[:id])
164 unless @problem.available or session[:admin]
158 unless @problem.available or session[:admin]
165 redirect_to :controller => 'main', :action => 'list'
159 redirect_to :controller => 'main', :action => 'list'
166 return
160 return
167 end
161 end
168 @submissions = Submission.includes(:user).includes(:language).where(problem_id: params[:id]).order(:user_id,:id)
162 @submissions = Submission.includes(:user).includes(:language).where(problem_id: params[:id]).order(:user_id,:id)
169
163
170 #stat summary
164 #stat summary
171 range =65
165 range =65
172 @histogram = { data: Array.new(range,0), summary: {} }
166 @histogram = { data: Array.new(range,0), summary: {} }
173 user = Hash.new(0)
167 user = Hash.new(0)
174 @submissions.find_each do |sub|
168 @submissions.find_each do |sub|
175 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
169 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
176 @histogram[:data][d.to_i] += 1 if d < range
170 @histogram[:data][d.to_i] += 1 if d < range
177 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
171 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
178 end
172 end
179 @histogram[:summary][:max] = [@histogram[:data].max,1].max
173 @histogram[:summary][:max] = [@histogram[:data].max,1].max
180
174
181 @summary = { attempt: user.count, solve: 0 }
175 @summary = { attempt: user.count, solve: 0 }
182 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
176 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
183 end
177 end
184
178
185 def manage
179 def manage
186 @problems = Problem.order(date_added: :desc)
180 @problems = Problem.order(date_added: :desc)
187 end
181 end
188
182
189 def do_manage
183 def do_manage
190 if params.has_key? 'change_date_added' and params[:date_added].strip.empty? == false
184 if params.has_key? 'change_date_added' and params[:date_added].strip.empty? == false
191 change_date_added
185 change_date_added
192 elsif params.has_key? 'add_to_contest'
186 elsif params.has_key? 'add_to_contest'
193 add_to_contest
187 add_to_contest
194 elsif params.has_key? 'enable_problem'
188 elsif params.has_key? 'enable_problem'
195 set_available(true)
189 set_available(true)
196 elsif params.has_key? 'disable_problem'
190 elsif params.has_key? 'disable_problem'
197 set_available(false)
191 set_available(false)
198 elsif params.has_key? 'add_group'
192 elsif params.has_key? 'add_group'
199 group = Group.find(params[:group_id])
193 group = Group.find(params[:group_id])
200 ok = []
194 ok = []
201 failed = []
195 failed = []
202 get_problems_from_params.each do |p|
196 get_problems_from_params.each do |p|
203 begin
197 begin
204 group.problems << p
198 group.problems << p
205 ok << p.full_name
199 ok << p.full_name
206 rescue => e
200 rescue => e
207 failed << p.full_name
201 failed << p.full_name
208 end
202 end
209 end
203 end
210 flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0
204 flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0
211 flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0
205 flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0
212 elsif params.has_key? 'add_tags'
206 elsif params.has_key? 'add_tags'
213 get_problems_from_params.each do |p|
207 get_problems_from_params.each do |p|
214 p.tag_ids += params[:tag_ids]
208 p.tag_ids += params[:tag_ids]
215 end
209 end
216 end
210 end
217
211
218 redirect_to :action => 'manage'
212 redirect_to :action => 'manage'
219 end
213 end
220
214
221 def import
215 def import
222 @allow_test_pair_import = allow_test_pair_import?
216 @allow_test_pair_import = allow_test_pair_import?
223 end
217 end
224
218
@@ -1,118 +1,111
1 class TestController < ApplicationController
1 class TestController < ApplicationController
2
2
3 before_action :authenticate, :check_viewability
3 before_action :authenticate, :check_viewability
4
4
5 - #
6 - # COMMENT OUT: filter in each action instead
7 - #
8 - # before_filter :verify_time_limit, :only => [:submit]
9 -
10 - verify :method => :post, :only => [:submit],
11 - :redirect_to => { :action => :index }
12
5
13 def index
6 def index
14 prepare_index_information
7 prepare_index_information
15 end
8 end
16
9
17 def submit
10 def submit
18 @user = User.find(session[:user_id])
11 @user = User.find(session[:user_id])
19
12
20 @submitted_test_request = TestRequest.new_from_form_params(@user,params[:test_request])
13 @submitted_test_request = TestRequest.new_from_form_params(@user,params[:test_request])
21
14
22 if ! @submitted_test_request.errors.empty?
15 if ! @submitted_test_request.errors.empty?
23 prepare_index_information
16 prepare_index_information
24 render :action => 'index' and return
17 render :action => 'index' and return
25 end
18 end
26
19
27 if GraderConfiguration.time_limit_mode?
20 if GraderConfiguration.time_limit_mode?
28 if @user.contest_finished?
21 if @user.contest_finished?
29 @submitted_test_request.errors.add(:base,'Contest is over.')
22 @submitted_test_request.errors.add(:base,'Contest is over.')
30 prepare_index_information
23 prepare_index_information
31 render :action => 'index' and return
24 render :action => 'index' and return
32 end
25 end
33
26
34 if !GraderConfiguration.allow_test_request(@user)
27 if !GraderConfiguration.allow_test_request(@user)
35 prepare_index_information
28 prepare_index_information
36 flash[:notice] = 'Test request is not allowed during the last 30 minutes'
29 flash[:notice] = 'Test request is not allowed during the last 30 minutes'
37 redirect_to :action => 'index' and return
30 redirect_to :action => 'index' and return
38 end
31 end
39 end
32 end
40
33
41 if @submitted_test_request.save
34 if @submitted_test_request.save
42 redirect_to :action => 'index'
35 redirect_to :action => 'index'
43 else
36 else
44 prepare_index_information
37 prepare_index_information
45 render :action => 'index'
38 render :action => 'index'
46 end
39 end
47 end
40 end
48
41
49 def read
42 def read
50 user = User.find(session[:user_id])
43 user = User.find(session[:user_id])
51 begin
44 begin
52 test_request = TestRequest.find(params[:id])
45 test_request = TestRequest.find(params[:id])
53 rescue
46 rescue
54 test_request = nil
47 test_request = nil
55 end
48 end
56 if test_request==nil or test_request.user_id != user.id
49 if test_request==nil or test_request.user_id != user.id
57 flash[:notice] = 'Invalid output'
50 flash[:notice] = 'Invalid output'
58 redirect_to :action => 'index'
51 redirect_to :action => 'index'
59 return
52 return
60 end
53 end
61 if test_request.output_file_name!=nil
54 if test_request.output_file_name!=nil
62 data = File.open(test_request.output_file_name).read(2048)
55 data = File.open(test_request.output_file_name).read(2048)
63 if data==nil
56 if data==nil
64 data=""
57 data=""
65 end
58 end
66 send_data(data,
59 send_data(data,
67 {:filename => 'output.txt',
60 {:filename => 'output.txt',
68 :type => 'text/plain'})
61 :type => 'text/plain'})
69 return
62 return
70 end
63 end
71 redirect_to :action => 'index'
64 redirect_to :action => 'index'
72 end
65 end
73
66
74 def result
67 def result
75 @user = User.find(session[:user_id])
68 @user = User.find(session[:user_id])
76 begin
69 begin
77 @test_request = TestRequest.find(params[:id])
70 @test_request = TestRequest.find(params[:id])
78 rescue
71 rescue
79 @test_request = nil
72 @test_request = nil
80 end
73 end
81 if @test_request==nil or @test_request.user_id != @user.id
74 if @test_request==nil or @test_request.user_id != @user.id
82 flash[:notice] = 'Invalid request'
75 flash[:notice] = 'Invalid request'
83 redirect_to :action => 'index'
76 redirect_to :action => 'index'
84 return
77 return
85 end
78 end
86 end
79 end
87
80
88 protected
81 protected
89
82
90 def prepare_index_information
83 def prepare_index_information
91 @user = User.find(session[:user_id])
84 @user = User.find(session[:user_id])
92 @submissions = Submission.find_last_for_all_available_problems(@user.id)
85 @submissions = Submission.find_last_for_all_available_problems(@user.id)
93 all_problems = @submissions.collect { |submission| submission.problem }
86 all_problems = @submissions.collect { |submission| submission.problem }
94 @problems = []
87 @problems = []
95 all_problems.each do |problem|
88 all_problems.each do |problem|
96 if problem.test_allowed
89 if problem.test_allowed
97 @problems << problem
90 @problems << problem
98 end
91 end
99 end
92 end
100 @test_requests = []
93 @test_requests = []
101 @user.test_requests.each do |ts|
94 @user.test_requests.each do |ts|
102 if ts.problem and ts.problem.available
95 if ts.problem and ts.problem.available
103 @test_requests << ts
96 @test_requests << ts
104 end
97 end
105 end
98 end
106 end
99 end
107
100
108 def check_viewability
101 def check_viewability
109 user = User.find(session[:user_id])
102 user = User.find(session[:user_id])
110 if !GraderConfiguration.show_tasks_to?(user)
103 if !GraderConfiguration.show_tasks_to?(user)
111 redirect_to :controller => 'main', :action => 'list'
104 redirect_to :controller => 'main', :action => 'list'
112 end
105 end
113 if (!GraderConfiguration.show_submitbox_to?(user)) and (action_name=='submit')
106 if (!GraderConfiguration.show_submitbox_to?(user)) and (action_name=='submit')
114 redirect_to :controller => 'test', :action => 'index'
107 redirect_to :controller => 'test', :action => 'index'
115 end
108 end
116 end
109 end
117
110
118 end
111 end
@@ -1,209 +1,200
1 require 'csv'
1 require 'csv'
2
2
3 class UserAdminController < ApplicationController
3 class UserAdminController < ApplicationController
4
4
5 include MailHelperMethods
5 include MailHelperMethods
6
6
7 before_action :admin_authorization
7 before_action :admin_authorization
8
8
9 - # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
10 - verify :method => :post, :only => [
11 - :create, :create_from_list,
12 - :update,
13 - :manage_contest,
14 - :bulk_mail
15 - ],
16 - :redirect_to => { :action => :list }
17 -
18 def index
9 def index
19 @user_count = User.count
10 @user_count = User.count
20 if params[:page] == 'all'
11 if params[:page] == 'all'
21 @users = User.all
12 @users = User.all
22 @paginated = false
13 @paginated = false
23 else
14 else
24 @users = User.paginate :page => params[:page]
15 @users = User.paginate :page => params[:page]
25 @paginated = true
16 @paginated = true
26 end
17 end
27 @users = User.all
18 @users = User.all
28 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
19 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
29 @contests = Contest.enabled
20 @contests = Contest.enabled
30 end
21 end
31
22
32 def active
23 def active
33 sessions = ActiveRecord::SessionStore::Session.where("updated_at >= ?", 60.minutes.ago)
24 sessions = ActiveRecord::SessionStore::Session.where("updated_at >= ?", 60.minutes.ago)
34 @users = []
25 @users = []
35 sessions.each do |session|
26 sessions.each do |session|
36 if session.data[:user_id]
27 if session.data[:user_id]
37 @users << User.find(session.data[:user_id])
28 @users << User.find(session.data[:user_id])
38 end
29 end
39 end
30 end
40 end
31 end
41
32
42 def show
33 def show
43 @user = User.find(params[:id])
34 @user = User.find(params[:id])
44 end
35 end
45
36
46 def new
37 def new
47 @user = User.new
38 @user = User.new
48 end
39 end
49
40
50 def create
41 def create
51 @user = User.new(user_params)
42 @user = User.new(user_params)
52 @user.activated = true
43 @user.activated = true
53 if @user.save
44 if @user.save
54 flash[:notice] = 'User was successfully created.'
45 flash[:notice] = 'User was successfully created.'
55 redirect_to :action => 'index'
46 redirect_to :action => 'index'
56 else
47 else
57 render :action => 'new'
48 render :action => 'new'
58 end
49 end
59 end
50 end
60
51
61 def clear_last_ip
52 def clear_last_ip
62 @user = User.find(params[:id])
53 @user = User.find(params[:id])
63 @user.last_ip = nil
54 @user.last_ip = nil
64 @user.save
55 @user.save
65 redirect_to action: 'index', page: params[:page]
56 redirect_to action: 'index', page: params[:page]
66 end
57 end
67
58
68 def create_from_list
59 def create_from_list
69 lines = params[:user_list]
60 lines = params[:user_list]
70
61
71 note = []
62 note = []
72
63
73 lines.split("\n").each do |line|
64 lines.split("\n").each do |line|
74 items = line.chomp.split(',')
65 items = line.chomp.split(',')
75 if items.length>=2
66 if items.length>=2
76 login = items[0]
67 login = items[0]
77 full_name = items[1]
68 full_name = items[1]
78 remark =''
69 remark =''
79 user_alias = ''
70 user_alias = ''
80
71
81 added_random_password = false
72 added_random_password = false
82 if items.length >= 3 and items[2].chomp(" ").length > 0;
73 if items.length >= 3 and items[2].chomp(" ").length > 0;
83 password = items[2].chomp(" ")
74 password = items[2].chomp(" ")
84 else
75 else
85 password = random_password
76 password = random_password
86 add_random_password=true;
77 add_random_password=true;
87 end
78 end
88
79
89 if items.length>= 4 and items[3].chomp(" ").length > 0;
80 if items.length>= 4 and items[3].chomp(" ").length > 0;
90 user_alias = items[3].chomp(" ")
81 user_alias = items[3].chomp(" ")
91 else
82 else
92 user_alias = login
83 user_alias = login
93 end
84 end
94
85
95 if items.length>=5
86 if items.length>=5
96 remark = items[4].strip;
87 remark = items[4].strip;
97 end
88 end
98
89
99 user = User.find_by_login(login)
90 user = User.find_by_login(login)
100 if (user)
91 if (user)
101 user.full_name = full_name
92 user.full_name = full_name
102 user.password = password
93 user.password = password
103 user.remark = remark
94 user.remark = remark
104 else
95 else
105 user = User.new({:login => login,
96 user = User.new({:login => login,
106 :full_name => full_name,
97 :full_name => full_name,
107 :password => password,
98 :password => password,
108 :password_confirmation => password,
99 :password_confirmation => password,
109 :alias => user_alias,
100 :alias => user_alias,
110 :remark => remark})
101 :remark => remark})
111 end
102 end
112 user.activated = true
103 user.activated = true
113 user.save
104 user.save
114
105
115 if added_random_password
106 if added_random_password
116 note << "'#{login}' (+)"
107 note << "'#{login}' (+)"
117 else
108 else
118 note << login
109 note << login
119 end
110 end
120 end
111 end
121 end
112 end
122 flash[:success] = 'User(s) ' + note.join(', ') +
113 flash[:success] = 'User(s) ' + note.join(', ') +
123 ' were successfully created. ' +
114 ' were successfully created. ' +
124 '( (+) - created with random passwords.)'
115 '( (+) - created with random passwords.)'
125 redirect_to :action => 'index'
116 redirect_to :action => 'index'
126 end
117 end
127
118
128 def edit
119 def edit
129 @user = User.find(params[:id])
120 @user = User.find(params[:id])
130 end
121 end
131
122
132 def update
123 def update
133 @user = User.find(params[:id])
124 @user = User.find(params[:id])
134 if @user.update_attributes(user_params)
125 if @user.update_attributes(user_params)
135 flash[:notice] = 'User was successfully updated.'
126 flash[:notice] = 'User was successfully updated.'
136 redirect_to :action => 'show', :id => @user
127 redirect_to :action => 'show', :id => @user
137 else
128 else
138 render :action => 'edit'
129 render :action => 'edit'
139 end
130 end
140 end
131 end
141
132
142 def destroy
133 def destroy
143 User.find(params[:id]).destroy
134 User.find(params[:id]).destroy
144 redirect_to :action => 'index'
135 redirect_to :action => 'index'
145 end
136 end
146
137
147 def user_stat
138 def user_stat
148 if params[:commit] == 'download csv'
139 if params[:commit] == 'download csv'
149 @problems = Problem.all
140 @problems = Problem.all
150 else
141 else
151 @problems = Problem.available_problems
142 @problems = Problem.available_problems
152 end
143 end
153 @users = User.includes(:contests, :contest_stat).where(enabled: true)
144 @users = User.includes(:contests, :contest_stat).where(enabled: true)
154 @scorearray = Array.new
145 @scorearray = Array.new
155 @users.each do |u|
146 @users.each do |u|
156 ustat = Array.new
147 ustat = Array.new
157 ustat[0] = u
148 ustat[0] = u
158 @problems.each do |p|
149 @problems.each do |p|
159 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
150 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
160 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
151 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
161 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
152 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
162 else
153 else
163 ustat << [0,false]
154 ustat << [0,false]
164 end
155 end
165 end
156 end
166 @scorearray << ustat
157 @scorearray << ustat
167 end
158 end
168 if params[:commit] == 'download csv' then
159 if params[:commit] == 'download csv' then
169 csv = gen_csv_from_scorearray(@scorearray,@problems)
160 csv = gen_csv_from_scorearray(@scorearray,@problems)
170 send_data csv, filename: 'last_score.csv'
161 send_data csv, filename: 'last_score.csv'
171 else
162 else
172 render template: 'user_admin/user_stat'
163 render template: 'user_admin/user_stat'
173 end
164 end
174 end
165 end
175
166
176 def user_stat_max
167 def user_stat_max
177 if params[:commit] == 'download csv'
168 if params[:commit] == 'download csv'
178 @problems = Problem.all
169 @problems = Problem.all
179 else
170 else
180 @problems = Problem.available_problems
171 @problems = Problem.available_problems
181 end
172 end
182 @users = User.includes(:contests).includes(:contest_stat).all
173 @users = User.includes(:contests).includes(:contest_stat).all
183 @scorearray = Array.new
174 @scorearray = Array.new
184 #set up range from param
175 #set up range from param
185 since_id = params.fetch(:since_id, 0).to_i
176 since_id = params.fetch(:since_id, 0).to_i
186 until_id = params.fetch(:until_id, 0).to_i
177 until_id = params.fetch(:until_id, 0).to_i
187 @users.each do |u|
178 @users.each do |u|
188 ustat = Array.new
179 ustat = Array.new
189 ustat[0] = u
180 ustat[0] = u
190 @problems.each do |p|
181 @problems.each do |p|
191 max_points = 0
182 max_points = 0
192 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
183 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
193 max_points = sub.points if sub and sub.points and (sub.points > max_points)
184 max_points = sub.points if sub and sub.points and (sub.points > max_points)
194 end
185 end
195 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
186 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
196 end
187 end
197 @scorearray << ustat
188 @scorearray << ustat
198 end
189 end
199
190
200 if params[:commit] == 'download csv' then
191 if params[:commit] == 'download csv' then
201 csv = gen_csv_from_scorearray(@scorearray,@problems)
192 csv = gen_csv_from_scorearray(@scorearray,@problems)
202 send_data csv, filename: 'max_score.csv'
193 send_data csv, filename: 'max_score.csv'
203 else
194 else
204 render template: 'user_admin/user_stat'
195 render template: 'user_admin/user_stat'
205 end
196 end
206 end
197 end
207
198
208 def import
199 def import
209 if params[:file]==''
200 if params[:file]==''
@@ -1,216 +1,213
1 require 'net/smtp'
1 require 'net/smtp'
2
2
3 class UsersController < ApplicationController
3 class UsersController < ApplicationController
4
4
5 include MailHelperMethods
5 include MailHelperMethods
6
6
7 before_action :authenticate, :except => [:new,
7 before_action :authenticate, :except => [:new,
8 :register,
8 :register,
9 :confirm,
9 :confirm,
10 :forget,
10 :forget,
11 :retrieve_password]
11 :retrieve_password]
12
12
13 before_action :verify_online_registration, :only => [:new,
13 before_action :verify_online_registration, :only => [:new,
14 :register,
14 :register,
15 :forget,
15 :forget,
16 :retrieve_password]
16 :retrieve_password]
17 before_action :authenticate, :profile_authorization, only: [:profile]
17 before_action :authenticate, :profile_authorization, only: [:profile]
18
18
19 before_action :admin_authorization, only: [:stat, :toggle_activate, :toggle_enable]
19 before_action :admin_authorization, only: [:stat, :toggle_activate, :toggle_enable]
20
20
21
21
22 - verify :method => :post, :only => [:chg_passwd],
23 - :redirect_to => { :action => :index }
24 -
25 #in_place_edit_for :user, :alias_for_editing
22 #in_place_edit_for :user, :alias_for_editing
26 #in_place_edit_for :user, :email_for_editing
23 #in_place_edit_for :user, :email_for_editing
27
24
28 def index
25 def index
29 if !GraderConfiguration['system.user_setting_enabled']
26 if !GraderConfiguration['system.user_setting_enabled']
30 redirect_to :controller => 'main', :action => 'list'
27 redirect_to :controller => 'main', :action => 'list'
31 else
28 else
32 @user = User.find(session[:user_id])
29 @user = User.find(session[:user_id])
33 end
30 end
34 end
31 end
35
32
36 def chg_passwd
33 def chg_passwd
37 user = User.find(session[:user_id])
34 user = User.find(session[:user_id])
38 user.password = params[:passwd]
35 user.password = params[:passwd]
39 user.password_confirmation = params[:passwd_verify]
36 user.password_confirmation = params[:passwd_verify]
40 if user.save
37 if user.save
41 flash[:notice] = 'password changed'
38 flash[:notice] = 'password changed'
42 else
39 else
43 flash[:notice] = 'Error: password changing failed'
40 flash[:notice] = 'Error: password changing failed'
44 end
41 end
45 redirect_to :action => 'index'
42 redirect_to :action => 'index'
46 end
43 end
47
44
48 def new
45 def new
49 @user = User.new
46 @user = User.new
50 render :action => 'new', :layout => 'empty'
47 render :action => 'new', :layout => 'empty'
51 end
48 end
52
49
53 def register
50 def register
54 if(params[:cancel])
51 if(params[:cancel])
55 redirect_to :controller => 'main', :action => 'login'
52 redirect_to :controller => 'main', :action => 'login'
56 return
53 return
57 end
54 end
58 @user = User.new(user_params)
55 @user = User.new(user_params)
59 @user.password_confirmation = @user.password = User.random_password
56 @user.password_confirmation = @user.password = User.random_password
60 @user.activated = false
57 @user.activated = false
61 if (@user.valid?) and (@user.save)
58 if (@user.valid?) and (@user.save)
62 if send_confirmation_email(@user)
59 if send_confirmation_email(@user)
63 render :action => 'new_splash', :layout => 'empty'
60 render :action => 'new_splash', :layout => 'empty'
64 else
61 else
65 @admin_email = GraderConfiguration['system.admin_email']
62 @admin_email = GraderConfiguration['system.admin_email']
66 render :action => 'email_error', :layout => 'empty'
63 render :action => 'email_error', :layout => 'empty'
67 end
64 end
68 else
65 else
69 @user.errors.add(:base,"Email cannot be blank") if @user.email==''
66 @user.errors.add(:base,"Email cannot be blank") if @user.email==''
70 render :action => 'new', :layout => 'empty'
67 render :action => 'new', :layout => 'empty'
71 end
68 end
72 end
69 end
73
70
74 def confirm
71 def confirm
75 login = params[:login]
72 login = params[:login]
76 key = params[:activation]
73 key = params[:activation]
77 @user = User.find_by_login(login)
74 @user = User.find_by_login(login)
78 if (@user) and (@user.verify_activation_key(key))
75 if (@user) and (@user.verify_activation_key(key))
79 if @user.valid? # check uniquenss of email
76 if @user.valid? # check uniquenss of email
80 @user.activated = true
77 @user.activated = true
81 @user.save
78 @user.save
82 @result = :successful
79 @result = :successful
83 else
80 else
84 @result = :email_used
81 @result = :email_used
85 end
82 end
86 else
83 else
87 @result = :failed
84 @result = :failed
88 end
85 end
89 render :action => 'confirm', :layout => 'empty'
86 render :action => 'confirm', :layout => 'empty'
90 end
87 end
91
88
92 def forget
89 def forget
93 render :action => 'forget', :layout => 'empty'
90 render :action => 'forget', :layout => 'empty'
94 end
91 end
95
92
96 def retrieve_password
93 def retrieve_password
97 email = params[:email]
94 email = params[:email]
98 user = User.find_by_email(email)
95 user = User.find_by_email(email)
99 if user
96 if user
100 last_updated_time = user.updated_at || user.created_at || (Time.now.gmtime - 1.hour)
97 last_updated_time = user.updated_at || user.created_at || (Time.now.gmtime - 1.hour)
101 if last_updated_time > Time.now.gmtime - 5.minutes
98 if last_updated_time > Time.now.gmtime - 5.minutes
102 flash[:notice] = 'The account has recently created or new password has recently been requested. Please wait for 5 minutes'
99 flash[:notice] = 'The account has recently created or new password has recently been requested. Please wait for 5 minutes'
103 else
100 else
104 user.password = user.password_confirmation = User.random_password
101 user.password = user.password_confirmation = User.random_password
105 user.save
102 user.save
106 send_new_password_email(user)
103 send_new_password_email(user)
107 flash[:notice] = 'New password has been mailed to you.'
104 flash[:notice] = 'New password has been mailed to you.'
108 end
105 end
109 else
106 else
110 flash[:notice] = I18n.t 'registration.password_retrieval.no_email'
107 flash[:notice] = I18n.t 'registration.password_retrieval.no_email'
111 end
108 end
112 redirect_to :action => 'forget'
109 redirect_to :action => 'forget'
113 end
110 end
114
111
115 def stat
112 def stat
116 @user = User.find(params[:id])
113 @user = User.find(params[:id])
117 @submission = Submission.joins(:problem).where(user_id: params[:id])
114 @submission = Submission.joins(:problem).where(user_id: params[:id])
118 @submission = @submission.where('problems.available = true') unless current_user.admin?
115 @submission = @submission.where('problems.available = true') unless current_user.admin?
119
116
120 range = 120
117 range = 120
121 @histogram = { data: Array.new(range,0), summary: {} }
118 @histogram = { data: Array.new(range,0), summary: {} }
122 @summary = {count: 0, solve: 0, attempt: 0}
119 @summary = {count: 0, solve: 0, attempt: 0}
123 problem = Hash.new(0)
120 problem = Hash.new(0)
124
121
125 @submission.find_each do |sub|
122 @submission.find_each do |sub|
126 #histogram
123 #histogram
127 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
124 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
128 @histogram[:data][d.to_i] += 1 if d < range
125 @histogram[:data][d.to_i] += 1 if d < range
129
126
130 @summary[:count] += 1
127 @summary[:count] += 1
131 next unless sub.problem
128 next unless sub.problem
132 problem[sub.problem] = [problem[sub.problem], ( (sub.try(:points) || 0) >= sub.problem.full_score) ? 1 : 0].max
129 problem[sub.problem] = [problem[sub.problem], ( (sub.try(:points) || 0) >= sub.problem.full_score) ? 1 : 0].max
133 end
130 end
134
131
135 @histogram[:summary][:max] = [@histogram[:data].max,1].max
132 @histogram[:summary][:max] = [@histogram[:data].max,1].max
136 @summary[:attempt] = problem.count
133 @summary[:attempt] = problem.count
137 problem.each_value { |v| @summary[:solve] += 1 if v == 1 }
134 problem.each_value { |v| @summary[:solve] += 1 if v == 1 }
138 end
135 end
139
136
140 def toggle_activate
137 def toggle_activate
141 @user = User.find(params[:id])
138 @user = User.find(params[:id])
142 @user.update_attributes( activated: !@user.activated? )
139 @user.update_attributes( activated: !@user.activated? )
143 respond_to do |format|
140 respond_to do |format|
144 format.js { render partial: 'toggle_button',
141 format.js { render partial: 'toggle_button',
145 locals: {button_id: "#toggle_activate_user_#{@user.id}",button_on: @user.activated? } }
142 locals: {button_id: "#toggle_activate_user_#{@user.id}",button_on: @user.activated? } }
146 end
143 end
147 end
144 end
148
145
149 def toggle_enable
146 def toggle_enable
150 @user = User.find(params[:id])
147 @user = User.find(params[:id])
151 @user.update_attributes( enabled: !@user.enabled? )
148 @user.update_attributes( enabled: !@user.enabled? )
152 respond_to do |format|
149 respond_to do |format|
153 format.js { render partial: 'toggle_button',
150 format.js { render partial: 'toggle_button',
154 locals: {button_id: "#toggle_enable_user_#{@user.id}",button_on: @user.enabled? } }
151 locals: {button_id: "#toggle_enable_user_#{@user.id}",button_on: @user.enabled? } }
155 end
152 end
156 end
153 end
157
154
158 protected
155 protected
159
156
160 def verify_online_registration
157 def verify_online_registration
161 if !GraderConfiguration['system.online_registration']
158 if !GraderConfiguration['system.online_registration']
162 redirect_to :controller => 'main', :action => 'login'
159 redirect_to :controller => 'main', :action => 'login'
163 end
160 end
164 end
161 end
165
162
166 def send_confirmation_email(user)
163 def send_confirmation_email(user)
167 contest_name = GraderConfiguration['contest.name']
164 contest_name = GraderConfiguration['contest.name']
168 activation_url = url_for(:action => 'confirm',
165 activation_url = url_for(:action => 'confirm',
169 :login => user.login,
166 :login => user.login,
170 :activation => user.activation_key)
167 :activation => user.activation_key)
171 home_url = url_for(:controller => 'main', :action => 'index')
168 home_url = url_for(:controller => 'main', :action => 'index')
172 mail_subject = "[#{contest_name}] Confirmation"
169 mail_subject = "[#{contest_name}] Confirmation"
173 mail_body = t('registration.email_body', {
170 mail_body = t('registration.email_body', {
174 :full_name => user.full_name,
171 :full_name => user.full_name,
175 :contest_name => contest_name,
172 :contest_name => contest_name,
176 :login => user.login,
173 :login => user.login,
177 :password => user.password,
174 :password => user.password,
178 :activation_url => activation_url,
175 :activation_url => activation_url,
179 :admin_email => GraderConfiguration['system.admin_email']
176 :admin_email => GraderConfiguration['system.admin_email']
180 })
177 })
181
178
182 logger.info mail_body
179 logger.info mail_body
183
180
184 send_mail(user.email, mail_subject, mail_body)
181 send_mail(user.email, mail_subject, mail_body)
185 end
182 end
186
183
187 def send_new_password_email(user)
184 def send_new_password_email(user)
188 contest_name = GraderConfiguration['contest.name']
185 contest_name = GraderConfiguration['contest.name']
189 mail_subject = "[#{contest_name}] Password recovery"
186 mail_subject = "[#{contest_name}] Password recovery"
190 mail_body = t('registration.password_retrieval.email_body', {
187 mail_body = t('registration.password_retrieval.email_body', {
191 :full_name => user.full_name,
188 :full_name => user.full_name,
192 :contest_name => contest_name,
189 :contest_name => contest_name,
193 :login => user.login,
190 :login => user.login,
194 :password => user.password,
191 :password => user.password,
195 :admin_email => GraderConfiguration['system.admin_email']
192 :admin_email => GraderConfiguration['system.admin_email']
196 })
193 })
197
194
198 logger.info mail_body
195 logger.info mail_body
199
196
200 send_mail(user.email, mail_subject, mail_body)
197 send_mail(user.email, mail_subject, mail_body)
201 end
198 end
202
199
203 # allow viewing of regular user profile only when options allow so
200 # allow viewing of regular user profile only when options allow so
204 # only admins can view admins profile
201 # only admins can view admins profile
205 def profile_authorization
202 def profile_authorization
206 #if view admins' profile, allow only admin
203 #if view admins' profile, allow only admin
207 return false unless(params[:id])
204 return false unless(params[:id])
208 user = User.find(params[:id])
205 user = User.find(params[:id])
209 return false unless user
206 return false unless user
210 return admin_authorization if user.admin?
207 return admin_authorization if user.admin?
211 return true if GraderConfiguration["right.user_view_submission"]
208 return true if GraderConfiguration["right.user_view_submission"]
212
209
213 #finally, we allow only admin
210 #finally, we allow only admin
214 admin_authorization
211 admin_authorization
215 end
212 end
216
213
@@ -1,13 +1,13
1 class Group < ActiveRecord::Base
1 class Group < ActiveRecord::Base
2 - has_many :groups_problems, class_name: GroupProblem
2 + has_many :groups_problems, class_name: 'GroupProblem'
3 has_many :problems, :through => :groups_problems
3 has_many :problems, :through => :groups_problems
4
4
5 - has_many :groups_users, class_name: GroupUser
5 + has_many :groups_users, class_name: 'GroupUser'
6 has_many :users, :through => :groups_users
6 has_many :users, :through => :groups_users
7
7
8 #has_and_belongs_to_many :problems
8 #has_and_belongs_to_many :problems
9 #has_and_belongs_to_many :users
9 #has_and_belongs_to_many :users
10
10
11
11
12 end
12 end
13
13
@@ -1,146 +1,146
1 class Problem < ActiveRecord::Base
1 class Problem < ActiveRecord::Base
2
2
3 belongs_to :description
3 belongs_to :description
4 has_and_belongs_to_many :contests, :uniq => true
4 has_and_belongs_to_many :contests, :uniq => true
5
5
6 #has_and_belongs_to_many :groups
6 #has_and_belongs_to_many :groups
7 - has_many :groups_problems, class_name: GroupProblem
7 + has_many :groups_problems, class_name: 'GroupProblem'
8 has_many :groups, :through => :groups_problems
8 has_many :groups, :through => :groups_problems
9
9
10 - has_many :problems_tags, class_name: ProblemTag
10 + has_many :problems_tags, class_name: 'ProblemTag'
11 has_many :tags, through: :problems_tags
11 has_many :tags, through: :problems_tags
12
12
13 has_many :test_pairs, :dependent => :delete_all
13 has_many :test_pairs, :dependent => :delete_all
14 has_many :testcases, :dependent => :destroy
14 has_many :testcases, :dependent => :destroy
15
15
16 has_many :submissions
16 has_many :submissions
17
17
18 validates_presence_of :name
18 validates_presence_of :name
19 validates_format_of :name, :with => /\A\w+\z/
19 validates_format_of :name, :with => /\A\w+\z/
20 validates_presence_of :full_name
20 validates_presence_of :full_name
21
21
22 scope :available, -> { where(available: true) }
22 scope :available, -> { where(available: true) }
23
23
24 DEFAULT_TIME_LIMIT = 1
24 DEFAULT_TIME_LIMIT = 1
25 DEFAULT_MEMORY_LIMIT = 32
25 DEFAULT_MEMORY_LIMIT = 32
26
26
27 def self.available_problems
27 def self.available_problems
28 available.order(date_added: :desc).order(:name)
28 available.order(date_added: :desc).order(:name)
29 #Problem.available.all(:order => "date_added DESC, name ASC")
29 #Problem.available.all(:order => "date_added DESC, name ASC")
30 end
30 end
31
31
32 def self.create_from_import_form_params(params, old_problem=nil)
32 def self.create_from_import_form_params(params, old_problem=nil)
33 org_problem = old_problem || Problem.new
33 org_problem = old_problem || Problem.new
34 import_params, problem = Problem.extract_params_and_check(params,
34 import_params, problem = Problem.extract_params_and_check(params,
35 org_problem)
35 org_problem)
36
36
37 if !problem.errors.empty?
37 if !problem.errors.empty?
38 return problem, 'Error importing'
38 return problem, 'Error importing'
39 end
39 end
40
40
41 problem.full_score = 100
41 problem.full_score = 100
42 problem.date_added = Time.new
42 problem.date_added = Time.new
43 problem.test_allowed = true
43 problem.test_allowed = true
44 problem.output_only = false
44 problem.output_only = false
45 problem.available = false
45 problem.available = false
46
46
47 if not problem.save
47 if not problem.save
48 return problem, 'Error importing'
48 return problem, 'Error importing'
49 end
49 end
50
50
51 import_to_db = params.has_key? :import_to_db
51 import_to_db = params.has_key? :import_to_db
52
52
53 importer = TestdataImporter.new(problem)
53 importer = TestdataImporter.new(problem)
54
54
55 if not importer.import_from_file(import_params[:file],
55 if not importer.import_from_file(import_params[:file],
56 import_params[:time_limit],
56 import_params[:time_limit],
57 import_params[:memory_limit],
57 import_params[:memory_limit],
58 import_params[:checker_name],
58 import_params[:checker_name],
59 import_to_db)
59 import_to_db)
60 problem.errors.add(:base,'Import error.')
60 problem.errors.add(:base,'Import error.')
61 end
61 end
62
62
63 return problem, importer.log_msg
63 return problem, importer.log_msg
64 end
64 end
65
65
66 def self.download_file_basedir
66 def self.download_file_basedir
67 return "#{Rails.root}/data/tasks"
67 return "#{Rails.root}/data/tasks"
68 end
68 end
69
69
70 def get_submission_stat
70 def get_submission_stat
71 result = Hash.new
71 result = Hash.new
72 #total number of submission
72 #total number of submission
73 result[:total_sub] = Submission.where(problem_id: self.id).count
73 result[:total_sub] = Submission.where(problem_id: self.id).count
74 result[:attempted_user] = Submission.where(problem_id: self.id).group(:user_id)
74 result[:attempted_user] = Submission.where(problem_id: self.id).group(:user_id)
75 result[:pass] = Submission.where(problem_id: self.id).where("points >= ?",self.full_score).count
75 result[:pass] = Submission.where(problem_id: self.id).where("points >= ?",self.full_score).count
76 return result
76 return result
77 end
77 end
78
78
79 def long_name
79 def long_name
80 "[#{name}] #{full_name}"
80 "[#{name}] #{full_name}"
81 end
81 end
82
82
83 protected
83 protected
84
84
85 def self.to_i_or_default(st, default)
85 def self.to_i_or_default(st, default)
86 if st!=''
86 if st!=''
87 result = st.to_i
87 result = st.to_i
88 end
88 end
89 result ||= default
89 result ||= default
90 end
90 end
91
91
92 def self.to_f_or_default(st, default)
92 def self.to_f_or_default(st, default)
93 if st!=''
93 if st!=''
94 result = st.to_f
94 result = st.to_f
95 end
95 end
96 result ||= default
96 result ||= default
97 end
97 end
98
98
99 def self.extract_params_and_check(params, problem)
99 def self.extract_params_and_check(params, problem)
100 time_limit = Problem.to_f_or_default(params[:time_limit],
100 time_limit = Problem.to_f_or_default(params[:time_limit],
101 DEFAULT_TIME_LIMIT)
101 DEFAULT_TIME_LIMIT)
102 memory_limit = Problem.to_i_or_default(params[:memory_limit],
102 memory_limit = Problem.to_i_or_default(params[:memory_limit],
103 DEFAULT_MEMORY_LIMIT)
103 DEFAULT_MEMORY_LIMIT)
104
104
105 if time_limit<=0 or time_limit >60
105 if time_limit<=0 or time_limit >60
106 problem.errors.add(:base,'Time limit out of range.')
106 problem.errors.add(:base,'Time limit out of range.')
107 end
107 end
108
108
109 if memory_limit==0 and params[:memory_limit]!='0'
109 if memory_limit==0 and params[:memory_limit]!='0'
110 problem.errors.add(:base,'Memory limit format errors.')
110 problem.errors.add(:base,'Memory limit format errors.')
111 elsif memory_limit<=0 or memory_limit >512
111 elsif memory_limit<=0 or memory_limit >512
112 problem.errors.add(:base,'Memory limit out of range.')
112 problem.errors.add(:base,'Memory limit out of range.')
113 end
113 end
114
114
115 if params[:file]==nil or params[:file]==''
115 if params[:file]==nil or params[:file]==''
116 problem.errors.add(:base,'No testdata file.')
116 problem.errors.add(:base,'No testdata file.')
117 end
117 end
118
118
119 checker_name = 'text'
119 checker_name = 'text'
120 if ['text','float'].include? params[:checker]
120 if ['text','float'].include? params[:checker]
121 checker_name = params[:checker]
121 checker_name = params[:checker]
122 end
122 end
123
123
124 file = params[:file]
124 file = params[:file]
125
125
126 if !problem.errors.empty?
126 if !problem.errors.empty?
127 return nil, problem
127 return nil, problem
128 end
128 end
129
129
130 problem.name = params[:name]
130 problem.name = params[:name]
131 if params[:full_name]!=''
131 if params[:full_name]!=''
132 problem.full_name = params[:full_name]
132 problem.full_name = params[:full_name]
133 else
133 else
134 problem.full_name = params[:name]
134 problem.full_name = params[:name]
135 end
135 end
136
136
137 return [{
137 return [{
138 :time_limit => time_limit,
138 :time_limit => time_limit,
139 :memory_limit => memory_limit,
139 :memory_limit => memory_limit,
140 :file => file,
140 :file => file,
141 :checker_name => checker_name
141 :checker_name => checker_name
142 },
142 },
143 problem]
143 problem]
144 end
144 end
145
145
146 end
146 end
@@ -1,4 +1,4
1 class Tag < ActiveRecord::Base
1 class Tag < ActiveRecord::Base
2 - has_many :problems_tags, class_name: ProblemTag
2 + has_many :problems_tags, class_name: 'ProblemTag'
3 has_many :problems, through: :problems_tags
3 has_many :problems, through: :problems_tags
4 end
4 end
@@ -1,204 +1,204
1 require 'digest/sha1'
1 require 'digest/sha1'
2 require 'net/pop'
2 require 'net/pop'
3 require 'net/https'
3 require 'net/https'
4 require 'net/http'
4 require 'net/http'
5 require 'json'
5 require 'json'
6
6
7 class User < ActiveRecord::Base
7 class User < ActiveRecord::Base
8
8
9 has_and_belongs_to_many :roles
9 has_and_belongs_to_many :roles
10
10
11 #has_and_belongs_to_many :groups
11 #has_and_belongs_to_many :groups
12 - has_many :groups_users, class_name: GroupUser
12 + has_many :groups_users, class_name: 'GroupUser'
13 has_many :groups, :through => :groups_users
13 has_many :groups, :through => :groups_users
14
14
15 has_many :test_requests, -> {order(submitted_at: :desc)}
15 has_many :test_requests, -> {order(submitted_at: :desc)}
16
16
17 has_many :messages, -> { order(created_at: :desc) },
17 has_many :messages, -> { order(created_at: :desc) },
18 :class_name => "Message",
18 :class_name => "Message",
19 :foreign_key => "sender_id"
19 :foreign_key => "sender_id"
20
20
21 has_many :replied_messages, -> { order(created_at: :desc) },
21 has_many :replied_messages, -> { order(created_at: :desc) },
22 :class_name => "Message",
22 :class_name => "Message",
23 :foreign_key => "receiver_id"
23 :foreign_key => "receiver_id"
24
24
25 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
25 has_one :contest_stat, :class_name => "UserContestStat", :dependent => :destroy
26
26
27 belongs_to :site
27 belongs_to :site
28 belongs_to :country
28 belongs_to :country
29
29
30 has_and_belongs_to_many :contests, -> { order(:name); uniq}
30 has_and_belongs_to_many :contests, -> { order(:name); uniq}
31
31
32 scope :activated_users, -> {where activated: true}
32 scope :activated_users, -> {where activated: true}
33
33
34 validates_presence_of :login
34 validates_presence_of :login
35 validates_uniqueness_of :login
35 validates_uniqueness_of :login
36 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
36 validates_format_of :login, :with => /\A[\_A-Za-z0-9]+\z/
37 validates_length_of :login, :within => 3..30
37 validates_length_of :login, :within => 3..30
38
38
39 validates_presence_of :full_name
39 validates_presence_of :full_name
40 validates_length_of :full_name, :minimum => 1
40 validates_length_of :full_name, :minimum => 1
41
41
42 validates_presence_of :password, :if => :password_required?
42 validates_presence_of :password, :if => :password_required?
43 validates_length_of :password, :within => 4..20, :if => :password_required?
43 validates_length_of :password, :within => 4..20, :if => :password_required?
44 validates_confirmation_of :password, :if => :password_required?
44 validates_confirmation_of :password, :if => :password_required?
45
45
46 validates_format_of :email,
46 validates_format_of :email,
47 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
47 :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i,
48 :if => :email_validation?
48 :if => :email_validation?
49 validate :uniqueness_of_email_from_activated_users,
49 validate :uniqueness_of_email_from_activated_users,
50 :if => :email_validation?
50 :if => :email_validation?
51 validate :enough_time_interval_between_same_email_registrations,
51 validate :enough_time_interval_between_same_email_registrations,
52 :if => :email_validation?
52 :if => :email_validation?
53
53
54 # these are for ytopc
54 # these are for ytopc
55 # disable for now
55 # disable for now
56 #validates_presence_of :province
56 #validates_presence_of :province
57
57
58 attr_accessor :password
58 attr_accessor :password
59
59
60 before_save :encrypt_new_password
60 before_save :encrypt_new_password
61 before_save :assign_default_site
61 before_save :assign_default_site
62 before_save :assign_default_contest
62 before_save :assign_default_contest
63
63
64 # this is for will_paginate
64 # this is for will_paginate
65 cattr_reader :per_page
65 cattr_reader :per_page
66 @@per_page = 50
66 @@per_page = 50
67
67
68 def self.authenticate(login, password)
68 def self.authenticate(login, password)
69 user = find_by_login(login)
69 user = find_by_login(login)
70 if user
70 if user
71 return user if user.authenticated?(password)
71 return user if user.authenticated?(password)
72 end
72 end
73 end
73 end
74
74
75 def authenticated?(password)
75 def authenticated?(password)
76 if self.activated
76 if self.activated
77 hashed_password == User.encrypt(password,self.salt)
77 hashed_password == User.encrypt(password,self.salt)
78 else
78 else
79 false
79 false
80 end
80 end
81 end
81 end
82
82
83 def admin?
83 def admin?
84 self.roles.detect {|r| r.name == 'admin' }
84 self.roles.detect {|r| r.name == 'admin' }
85 end
85 end
86
86
87 def email_for_editing
87 def email_for_editing
88 if self.email==nil
88 if self.email==nil
89 "(unknown)"
89 "(unknown)"
90 elsif self.email==''
90 elsif self.email==''
91 "(blank)"
91 "(blank)"
92 else
92 else
93 self.email
93 self.email
94 end
94 end
95 end
95 end
96
96
97 def email_for_editing=(e)
97 def email_for_editing=(e)
98 self.email=e
98 self.email=e
99 end
99 end
100
100
101 def alias_for_editing
101 def alias_for_editing
102 if self.alias==nil
102 if self.alias==nil
103 "(unknown)"
103 "(unknown)"
104 elsif self.alias==''
104 elsif self.alias==''
105 "(blank)"
105 "(blank)"
106 else
106 else
107 self.alias
107 self.alias
108 end
108 end
109 end
109 end
110
110
111 def alias_for_editing=(e)
111 def alias_for_editing=(e)
112 self.alias=e
112 self.alias=e
113 end
113 end
114
114
115 def activation_key
115 def activation_key
116 if self.hashed_password==nil
116 if self.hashed_password==nil
117 encrypt_new_password
117 encrypt_new_password
118 end
118 end
119 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
119 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
120 end
120 end
121
121
122 def verify_activation_key(key)
122 def verify_activation_key(key)
123 key == activation_key
123 key == activation_key
124 end
124 end
125
125
126 def self.random_password(length=5)
126 def self.random_password(length=5)
127 chars = 'abcdefghjkmnopqrstuvwxyz'
127 chars = 'abcdefghjkmnopqrstuvwxyz'
128 password = ''
128 password = ''
129 length.times { password << chars[rand(chars.length - 1)] }
129 length.times { password << chars[rand(chars.length - 1)] }
130 password
130 password
131 end
131 end
132
132
133 def self.find_non_admin_with_prefix(prefix='')
133 def self.find_non_admin_with_prefix(prefix='')
134 users = User.all
134 users = User.all
135 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
135 return users.find_all { |u| !(u.admin?) and u.login.index(prefix)==0 }
136 end
136 end
137
137
138 # Contest information
138 # Contest information
139
139
140 def self.find_users_with_no_contest()
140 def self.find_users_with_no_contest()
141 users = User.all
141 users = User.all
142 return users.find_all { |u| u.contests.length == 0 }
142 return users.find_all { |u| u.contests.length == 0 }
143 end
143 end
144
144
145
145
146 def contest_time_left
146 def contest_time_left
147 if GraderConfiguration.contest_mode?
147 if GraderConfiguration.contest_mode?
148 return nil if site==nil
148 return nil if site==nil
149 return site.time_left
149 return site.time_left
150 elsif GraderConfiguration.indv_contest_mode?
150 elsif GraderConfiguration.indv_contest_mode?
151 time_limit = GraderConfiguration.contest_time_limit
151 time_limit = GraderConfiguration.contest_time_limit
152 if time_limit == nil
152 if time_limit == nil
153 return nil
153 return nil
154 end
154 end
155 if contest_stat==nil or contest_stat.started_at==nil
155 if contest_stat==nil or contest_stat.started_at==nil
156 return (Time.now.gmtime + time_limit) - Time.now.gmtime
156 return (Time.now.gmtime + time_limit) - Time.now.gmtime
157 else
157 else
158 finish_time = contest_stat.started_at + time_limit
158 finish_time = contest_stat.started_at + time_limit
159 current_time = Time.now.gmtime
159 current_time = Time.now.gmtime
160 if current_time > finish_time
160 if current_time > finish_time
161 return 0
161 return 0
162 else
162 else
163 return finish_time - current_time
163 return finish_time - current_time
164 end
164 end
165 end
165 end
166 else
166 else
167 return nil
167 return nil
168 end
168 end
169 end
169 end
170
170
171 def contest_finished?
171 def contest_finished?
172 if GraderConfiguration.contest_mode?
172 if GraderConfiguration.contest_mode?
173 return false if site==nil
173 return false if site==nil
174 return site.finished?
174 return site.finished?
175 elsif GraderConfiguration.indv_contest_mode?
175 elsif GraderConfiguration.indv_contest_mode?
176 return false if self.contest_stat(true)==nil
176 return false if self.contest_stat(true)==nil
177 return contest_time_left == 0
177 return contest_time_left == 0
178 else
178 else
179 return false
179 return false
180 end
180 end
181 end
181 end
182
182
183 def contest_started?
183 def contest_started?
184 if GraderConfiguration.indv_contest_mode?
184 if GraderConfiguration.indv_contest_mode?
185 stat = self.contest_stat
185 stat = self.contest_stat
186 return ((stat != nil) and (stat.started_at != nil))
186 return ((stat != nil) and (stat.started_at != nil))
187 elsif GraderConfiguration.contest_mode?
187 elsif GraderConfiguration.contest_mode?
188 return true if site==nil
188 return true if site==nil
189 return site.started
189 return site.started
190 else
190 else
191 return true
191 return true
192 end
192 end
193 end
193 end
194
194
195 def update_start_time
195 def update_start_time
196 stat = self.contest_stat
196 stat = self.contest_stat
197 if stat.nil? or stat.started_at.nil?
197 if stat.nil? or stat.started_at.nil?
198 stat ||= UserContestStat.new(:user => self)
198 stat ||= UserContestStat.new(:user => self)
199 stat.started_at = Time.now.gmtime
199 stat.started_at = Time.now.gmtime
200 stat.save
200 stat.save
201 end
201 end
202 end
202 end
203
203
204 def problem_in_user_contests?(problem)
204 def problem_in_user_contests?(problem)
@@ -1,65 +1,65
1 - content_for :head do
1 - content_for :head do
2 = stylesheet_link_tag 'problems'
2 = stylesheet_link_tag 'problems'
3 %h1 Problems
3 %h1 Problems
4 %p
4 %p
5 = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-success btn-sm'
5 = link_to 'Import problems', {:action => 'import'}, class: 'btn btn-success btn-sm'
6 = link_to 'New problem', new_problem_path, class: 'btn btn-success btn-sm'
6 = link_to 'New problem', new_problem_path, class: 'btn btn-success btn-sm'
7 = link_to 'Bulk Manage', { action: 'manage'}, class: 'btn btn-info btn-sm'
7 = link_to 'Bulk Manage', { action: 'manage'}, class: 'btn btn-info btn-sm'
8 = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-default btn-sm'
8 = link_to 'Turn off all problems', {:action => 'turn_all_off'}, class: 'btn btn-default btn-sm'
9 = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-default btn-sm'
9 = link_to 'Turn on all problems', {:action => 'turn_all_on'}, class: 'btn btn-default btn-sm'
10 .submitbox
10 .submitbox
11 - = form_tag :action => 'quick_create' do
11 + = form_tag action: 'quick_create', controller: 'problems' do
12 %b Quick New:
12 %b Quick New:
13 %label{:for => "problem_name"} Name
13 %label{:for => "problem_name"} Name
14 = text_field 'problem', 'name'
14 = text_field 'problem', 'name'
15 |
15 |
16 %label{:for => "problem_full_name"} Full name
16 %label{:for => "problem_full_name"} Full name
17 = text_field 'problem', 'full_name'
17 = text_field 'problem', 'full_name'
18 = submit_tag "Create"
18 = submit_tag "Create"
19 %table.table.table-condense.table-hover
19 %table.table.table-condense.table-hover
20 %thead
20 %thead
21 %th Name
21 %th Name
22 %th Full name
22 %th Full name
23 %th.text-right Full score
23 %th.text-right Full score
24 %th Tags
24 %th Tags
25 %th
25 %th
26 Submit
26 Submit
27 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Admin can always submit to any problem' } [?]
27 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Admin can always submit to any problem' } [?]
28 %th Date added
28 %th Date added
29 %th.text-center
29 %th.text-center
30 Avail?
30 Avail?
31 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?]
31 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user submits to this problem?' } [?]
32 %th.text-center
32 %th.text-center
33 View Data?
33 View Data?
34 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?]
34 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user view the testcase of this problem?' } [?]
35 %th.text-center
35 %th.text-center
36 Test?
36 Test?
37 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
37 %sup{class: 'text-primary',data: {toggle: 'tooltip'}, title: 'Let user uses test interface on this problem?' } [?]
38 - if GraderConfiguration.multicontests?
38 - if GraderConfiguration.multicontests?
39 %th Contests
39 %th Contests
40 - for problem in @problems
40 - for problem in @problems
41 %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
41 %tr{:class => "#{(problem.available) ? "success" : "danger"}", :id => "prob-#{problem.id}", :name => "prob-#{problem.id}"}
42 - @problem=problem
42 - @problem=problem
43 %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1
43 %td= problem.name #in_place_editor_field :problem, :name, {}, :rows=>1
44 %td
44 %td
45 = problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1
45 = problem.full_name #in_place_editor_field :problem, :full_name, {}, :rows=>1
46 = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem
46 = link_to_description_if_any "[#{t 'main.problem_desc'}] <span class='glyphicon glyphicon-file'></span>".html_safe, problem
47 %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1
47 %td.text-right= problem.full_score #in_place_editor_field :problem, :full_score, {}, :rows=>1
48 %td
48 %td
49 - problem.tags.each do |t|
49 - problem.tags.each do |t|
50 - #%button.btn.btn-default.btn-xs= t.name
50 - #%button.btn.btn-default.btn-xs= t.name
51 %span.label.label-default= t.name
51 %span.label.label-default= t.name
52 %td= link_to "Submit", direct_edit_problem_submissions_path(problem,@current_user.id), class: 'btn btn-xs btn-primary'
52 %td= link_to "Submit", direct_edit_problem_submissions_path(problem,@current_user.id), class: 'btn btn-xs btn-primary'
53 %td= problem.date_added
53 %td= problem.date_added
54 %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}")
54 %td= toggle_button(@problem.available?, toggle_problem_path(@problem), "problem-avail-#{@problem.id}")
55 %td= toggle_button(@problem.view_testcase?, toggle_view_testcase_problem_path(@problem), "problem-view-testcase-#{@problem.id}")
55 %td= toggle_button(@problem.view_testcase?, toggle_view_testcase_problem_path(@problem), "problem-view-testcase-#{@problem.id}")
56 %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}")
56 %td= toggle_button(@problem.test_allowed?, toggle_test_problem_path(@problem), "problem-test-#{@problem.id}")
57 - if GraderConfiguration.multicontests?
57 - if GraderConfiguration.multicontests?
58 %td
58 %td
59 = problem.contests.collect { |c| c.name }.join(', ')
59 = problem.contests.collect { |c| c.name }.join(', ')
60 %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block'
60 %td= link_to 'Stat', {:action => 'stat', :id => problem.id}, class: 'btn btn-info btn-xs btn-block'
61 %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block'
61 %td= link_to 'Show', {:action => 'show', :id => problem}, class: 'btn btn-info btn-xs btn-block'
62 %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block'
62 %td= link_to 'Edit', {:action => 'edit', :id => problem}, class: 'btn btn-info btn-xs btn-block'
63 %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
63 %td= link_to 'Destroy', { :action => 'destroy', :id => problem }, :confirm => 'Are you sure?', :method => :delete, class: 'btn btn-danger btn-xs btn-block'
64 %br/
64 %br/
65 = link_to '[New problem]', :action => 'new'
65 = link_to '[New problem]', :action => 'new'
@@ -1,8 +1,8
1 <h1>New problem</h1>
1 <h1>New problem</h1>
2
2
3 <%= form_tag :action => 'create' do %>
3 <%= form_tag :action => 'create' do %>
4 <%= render :partial => 'form' %>
4 <%= render :partial => 'form' %>
5 <%= submit_tag "Create" %>
5 <%= submit_tag "Create" %>
6 <% end %>
6 <% end %>
7
7
8 - <%= link_to 'Back', :action => 'list' %>
8 + <%= link_to 'Back', problems_path %>
@@ -1,13 +1,13
1 %h1 Editing user
1 %h1 Editing user
2
2
3 = form_tag( {:action => 'update', :id => @user}, {class: 'form-horizontal'}) do
3 = form_tag( {:action => 'update', :id => @user}, {class: 'form-horizontal'}) do
4 = error_messages_for 'user'
4 = error_messages_for 'user'
5 = render partial: "form"
5 = render partial: "form"
6 .form-group
6 .form-group
7 .col-md-offset-2.col-md-4
7 .col-md-offset-2.col-md-4
8 = submit_tag "Edit", class: 'btn btn-primary'
8 = submit_tag "Edit", class: 'btn btn-primary'
9
9
10
10
11 = link_to 'Show', :action => 'show', :id => @user
11 = link_to 'Show', :action => 'show', :id => @user
12 |
12 |
13 - = link_to 'Back', :action => 'list'
13 + = link_to 'Back', :action => 'index'
@@ -1,25 +1,25
1 # Be sure to restart your server when you modify this file.
1 # Be sure to restart your server when you modify this file.
2 #
2 #
3 # This file contains migration options to ease your Rails 5.0 upgrade.
3 # This file contains migration options to ease your Rails 5.0 upgrade.
4 #
4 #
5 # Once upgraded flip defaults one by one to migrate to the new default.
5 # Once upgraded flip defaults one by one to migrate to the new default.
6 #
6 #
7 # Read the Guide for Upgrading Ruby on Rails for more info on each option.
7 # Read the Guide for Upgrading Ruby on Rails for more info on each option.
8
8
9 Rails.application.config.action_controller.raise_on_unfiltered_parameters = true
9 Rails.application.config.action_controller.raise_on_unfiltered_parameters = true
10
10
11 # Enable per-form CSRF tokens. Previous versions had false.
11 # Enable per-form CSRF tokens. Previous versions had false.
12 Rails.application.config.action_controller.per_form_csrf_tokens = false
12 Rails.application.config.action_controller.per_form_csrf_tokens = false
13
13
14 # Enable origin-checking CSRF mitigation. Previous versions had false.
14 # Enable origin-checking CSRF mitigation. Previous versions had false.
15 Rails.application.config.action_controller.forgery_protection_origin_check = false
15 Rails.application.config.action_controller.forgery_protection_origin_check = false
16
16
17 # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`.
17 # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`.
18 # Previous versions had false.
18 # Previous versions had false.
19 ActiveSupport.to_time_preserves_timezone = false
19 ActiveSupport.to_time_preserves_timezone = false
20
20
21 # Require `belongs_to` associations by default. Previous versions had false.
21 # Require `belongs_to` associations by default. Previous versions had false.
22 Rails.application.config.active_record.belongs_to_required_by_default = false
22 Rails.application.config.active_record.belongs_to_required_by_default = false
23
23
24 # Do not halt callback chains when a callback returns false. Previous versions had true.
24 # Do not halt callback chains when a callback returns false. Previous versions had true.
25 - ActiveSupport.halt_callback_chains_on_return_false = true
25 + # ActiveSupport.halt_callback_chains_on_return_false = true
@@ -1,167 +1,171
1 Rails.application.routes.draw do
1 Rails.application.routes.draw do
2 resources :tags
2 resources :tags
3 get "sources/direct_edit"
3 get "sources/direct_edit"
4
4
5 root :to => 'main#login'
5 root :to => 'main#login'
6
6
7 #logins
7 #logins
8 match 'login/login', to: 'login#login', via: [:get,:post]
8 match 'login/login', to: 'login#login', via: [:get,:post]
9
9
10
10
11 resources :contests
11 resources :contests
12
12
13 resources :sites
13 resources :sites
14
14
15 resources :test
15 resources :test
16
16
17 resources :messages do
17 resources :messages do
18 collection do
18 collection do
19 get 'console'
19 get 'console'
20 end
20 end
21 end
21 end
22
22
23 resources :announcements do
23 resources :announcements do
24 member do
24 member do
25 get 'toggle','toggle_front'
25 get 'toggle','toggle_front'
26 end
26 end
27 end
27 end
28
28
29 resources :problems do
29 resources :problems do
30 member do
30 member do
31 get 'toggle'
31 get 'toggle'
32 get 'toggle_test'
32 get 'toggle_test'
33 get 'toggle_view_testcase'
33 get 'toggle_view_testcase'
34 get 'stat'
34 get 'stat'
35 end
35 end
36 collection do
36 collection do
37 get 'turn_all_off'
37 get 'turn_all_off'
38 get 'turn_all_on'
38 get 'turn_all_on'
39 get 'import'
39 get 'import'
40 get 'manage'
40 get 'manage'
41 + get 'quick_create'
42 + post 'do_manage'
41 end
43 end
42 end
44 end
43
45
44 resources :groups do
46 resources :groups do
45 member do
47 member do
46 post 'add_user', to: 'groups#add_user', as: 'add_user'
48 post 'add_user', to: 'groups#add_user', as: 'add_user'
47 delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
49 delete 'remove_user/:user_id', to: 'groups#remove_user', as: 'remove_user'
48 delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user'
50 delete 'remove_all_user', to: 'groups#remove_all_user', as: 'remove_all_user'
49 post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
51 post 'add_problem', to: 'groups#add_problem', as: 'add_problem'
50 delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
52 delete 'remove_problem/:problem_id', to: 'groups#remove_problem', as: 'remove_problem'
51 delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem'
53 delete 'remove_all_problem', to: 'groups#remove_all_problem', as: 'remove_all_problem'
52 end
54 end
53 collection do
55 collection do
54
56
55 end
57 end
56 end
58 end
57
59
58 resources :testcases, only: [] do
60 resources :testcases, only: [] do
59 member do
61 member do
60 get 'download_input'
62 get 'download_input'
61 get 'download_sol'
63 get 'download_sol'
62 end
64 end
63 collection do
65 collection do
64 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
66 get 'show_problem/:problem_id(/:test_num)' => 'testcases#show_problem', as: 'show_problem'
65 end
67 end
66 end
68 end
67
69
68 resources :grader_configuration, controller: 'configurations'
70 resources :grader_configuration, controller: 'configurations'
69
71
70 resources :users do
72 resources :users do
71 member do
73 member do
72 get 'toggle_activate', 'toggle_enable'
74 get 'toggle_activate', 'toggle_enable'
73 get 'stat'
75 get 'stat'
74 end
76 end
75 end
77 end
76
78
77 resources :submissions do
79 resources :submissions do
78 member do
80 member do
79 get 'download'
81 get 'download'
80 get 'compiler_msg'
82 get 'compiler_msg'
81 get 'rejudge'
83 get 'rejudge'
82 end
84 end
83 collection do
85 collection do
84 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
86 get 'prob/:problem_id', to: 'submissions#index', as: 'problem'
85 get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
87 get 'direct_edit_problem/:problem_id(/:user_id)', to: 'submissions#direct_edit_problem', as: 'direct_edit_problem'
86 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
88 get 'get_latest_submission_status/:uid/:pid', to: 'submissions#get_latest_submission_status', as: 'get_latest_submission_status'
87 end
89 end
88 end
90 end
89
91
90
92
91 #user admin
93 #user admin
92 resources :user_admin do
94 resources :user_admin do
93 collection do
95 collection do
94 match 'bulk_manage', via: [:get, :post]
96 match 'bulk_manage', via: [:get, :post]
97 + get 'bulk_mail'
95 get 'user_stat'
98 get 'user_stat'
96 get 'import'
99 get 'import'
97 get 'new_list'
100 get 'new_list'
98 get 'admin'
101 get 'admin'
99 get 'random_all_passwords'
102 get 'random_all_passwords'
100 get 'active'
103 get 'active'
101 get 'mass_mailing'
104 get 'mass_mailing'
102 match 'create_from_list', via: [:get, :post]
105 match 'create_from_list', via: [:get, :post]
106 + post 'grant_admin'
103 end
107 end
104 member do
108 member do
105 get 'clear_last_ip'
109 get 'clear_last_ip'
106 end
110 end
107 end
111 end
108
112
109 resources :contest_management, only: [:index] do
113 resources :contest_management, only: [:index] do
110 collection do
114 collection do
111 get 'user_stat'
115 get 'user_stat'
112 get 'clear_stat'
116 get 'clear_stat'
113 get 'clear_all_stat'
117 get 'clear_all_stat'
114 end
118 end
115 end
119 end
116
120
117 #get 'user_admin', to: 'user_admin#index'
121 #get 'user_admin', to: 'user_admin#index'
118 #get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
122 #get 'user_admin/bulk_manage', to: 'user_admin#bulk_manage', as: 'bulk_manage_user_admin'
119 #post 'user_admin', to: 'user_admin#create'
123 #post 'user_admin', to: 'user_admin#create'
120 #delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy'
124 #delete 'user_admin/:id', to: 'user_admin#destroy', as: 'user_admin_destroy'
121
125
122 #singular resource
126 #singular resource
123 #---- BEWARE ---- singular resource maps to plural controller by default, we can override by provide controller name directly
127 #---- BEWARE ---- singular resource maps to plural controller by default, we can override by provide controller name directly
124 #report
128 #report
125 resource :report, only: [], controller: 'report' do
129 resource :report, only: [], controller: 'report' do
126 get 'login'
130 get 'login'
127 get 'multiple_login'
131 get 'multiple_login'
128 get 'problem_hof/:id', action: 'problem_hof'
132 get 'problem_hof/:id', action: 'problem_hof'
129 get 'current_score'
133 get 'current_score'
130 get 'max_score'
134 get 'max_score'
131 post 'show_max_score'
135 post 'show_max_score'
132 end
136 end
133 #get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
137 #get 'report/current_score', to: 'report#current_score', as: 'report_current_score'
134 #get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
138 #get 'report/problem_hof(/:id)', to: 'report#problem_hof', as: 'report_problem_hof'
135 #get "report/login"
139 #get "report/login"
136 #get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
140 #get 'report/max_score', to: 'report#max_score', as: 'report_max_score'
137 #post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
141 #post 'report/show_max_score', to: 'report#show_max_score', as: 'report_show_max_score'
138
142
139 resource :main, only: [], controller: 'main' do
143 resource :main, only: [], controller: 'main' do
140 get 'list'
144 get 'list'
141 get 'submission(/:id)', action: 'submission', as: 'main_submission'
145 get 'submission(/:id)', action: 'submission', as: 'main_submission'
142 post 'submit'
146 post 'submit'
143 get 'announcements'
147 get 'announcements'
144 get 'help'
148 get 'help'
145 end
149 end
146 #main
150 #main
147 #get "main/list"
151 #get "main/list"
148 #get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
152 #get 'main/submission(/:id)', to: 'main#submission', as: 'main_submission'
149 #post 'main/submit', to: 'main#submit'
153 #post 'main/submit', to: 'main#submit'
150 #get 'main/announcements', to: 'main#announcements'
154 #get 'main/announcements', to: 'main#announcements'
151
155
152
156
153 #
157 #
154 get 'tasks/view/:file.:ext' => 'tasks#view'
158 get 'tasks/view/:file.:ext' => 'tasks#view'
155 get 'tasks/download/:id/:file.:ext' => 'tasks#download'
159 get 'tasks/download/:id/:file.:ext' => 'tasks#download'
156 get 'heartbeat/:id/edit' => 'heartbeat#edit'
160 get 'heartbeat/:id/edit' => 'heartbeat#edit'
157
161
158 #grader
162 #grader
159 get 'graders/list', to: 'graders#list', as: 'grader_list'
163 get 'graders/list', to: 'graders#list', as: 'grader_list'
160
164
161
165
162 # See how all your routes lay out with "rake routes"
166 # See how all your routes lay out with "rake routes"
163
167
164 # This is a legacy wild controller route that's not recommended for RESTful applications.
168 # This is a legacy wild controller route that's not recommended for RESTful applications.
165 # Note: This route will make all actions in every controller accessible via GET requests.
169 # Note: This route will make all actions in every controller accessible via GET requests.
166 # match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
170 # match ':controller(/:action(/:id))(.:format)', via: [:get, :post]
167 end
171 end
@@ -1,27 +1,28
1 - ENV["RAILS_ENV"] = "test"
1 + ENV['RAILS_ENV'] ||= 'test'
2 - require File.expand_path('../../config/environment', __FILE__)
2 + require_relative '../config/environment'
3 require 'rails/test_help'
3 require 'rails/test_help'
4
4
5 #reporter for beautiful result
5 #reporter for beautiful result
6 require "minitest/reporters"
6 require "minitest/reporters"
7 Minitest::Reporters.use!
7 Minitest::Reporters.use!
8
8
9 module SignInHelper
9 module SignInHelper
10 def sign_in_as(user,password)
10 def sign_in_as(user,password)
11 post login_login_path, {login: user, password: password }
11 post login_login_path, {login: user, password: password }
12 end
12 end
13 end
13 end
14
14
15 class ActiveSupport::TestCase
15 class ActiveSupport::TestCase
16 include SignInHelper
16 include SignInHelper
17 # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
17 # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
18 #
18 #
19 # Note: You'll currently still have to declare fixtures explicitly in integration tests
19 # Note: You'll currently still have to declare fixtures explicitly in integration tests
20 # -- they do not yet inherit this setting
20 # -- they do not yet inherit this setting
21 fixtures :all
21 fixtures :all
22
22
23 # Add more helper methods to be used by all tests here...
23 # Add more helper methods to be used by all tests here...
24
24
25 - self.use_transactional_fixtures = true
25 + self.use_transactional_tests = true
26 + #self.use_instantiated_fixtures = false
26 self.use_instantiated_fixtures = false
27 self.use_instantiated_fixtures = false
27 end
28 end
deleted file
deleted file
You need to be logged in to leave comments. Login now