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

r695:55afb135c5f9 - - 12 files changed: 131 inserted, 68 deleted

@@ -1,91 +1,92
1 1 source 'https://rubygems.org'
2 2
3 3 #rails
4 4 gem 'rails', '~>4.2.0'
5 5 gem 'activerecord-session_store'
6 6
7 7
8 8 # Bundle edge Rails instead:
9 9 # gem 'rails', :git => 'git://github.com/rails/rails.git'
10 10
11 11 #---------------- database ---------------------
12 12 #the database
13 13 gem 'mysql2'
14 14 #for testing
15 15 gem 'sqlite3'
16 16 #for dumping database into yaml
17 17 gem 'yaml_db'
18 18
19 19 # Gems used only for assets and not required
20 20 # in production environments by default.
21 21 gem 'sass-rails'
22 22 gem 'coffee-rails'
23 23
24 24 # See https://github.com/sstephenson/execjs#readme for more supported runtimes
25 25 # gem 'therubyracer', :platforms => :ruby
26 26
27 27 gem 'uglifier'
28 28
29 29 gem 'haml'
30 30 gem 'haml-rails'
31 31 # gem 'prototype-rails'
32 32
33 33 # To use ActiveModel has_secure_password
34 34 # gem 'bcrypt-ruby', '~> 3.0.0'
35 35
36 36 # To use Jbuilder templates for JSON
37 37 # gem 'jbuilder'
38 38
39 39 # Use unicorn as the app server
40 40 # gem 'unicorn'
41 41
42 42 # Deploy with Capistrano
43 43 # gem 'capistrano'
44 44
45 45 # To use debugger
46 46 # gem 'debugger'
47 47 #
48 48
49 49 #in-place editor
50 50 gem 'best_in_place', '~> 3.0.1'
51 51
52 52 # jquery addition
53 53 gem 'jquery-rails'
54 54 gem 'jquery-ui-rails'
55 55 gem 'jquery-timepicker-addon-rails'
56 56 gem 'jquery-tablesorter'
57 57 gem 'jquery-countdown-rails'
58 58
59 59 #syntax highlighter
60 60 gem 'rouge'
61 61
62 - #add bootstrap
62 + #bootstrap add-ons
63 63 gem 'bootstrap-sass', '~> 3.2.0'
64 64 gem 'bootstrap-switch-rails'
65 65 gem 'bootstrap-toggle-rails'
66 66 gem 'autoprefixer-rails'
67 -
68 - #bootstrap sortable
69 67 gem 'momentjs-rails'
70 68 gem 'rails_bootstrap_sortable'
69 + gem 'bootstrap-datepicker-rails'
70 + gem 'bootstrap3-datetimepicker-rails'
71 + gem 'jquery-datatables-rails'
71 72
72 73 #----------- user interface -----------------
73 74 #select 2
74 75 gem 'select2-rails'
75 76 #ace editor
76 77 gem 'ace-rails-ap'
77 78 #paginator
78 79 gem 'will_paginate', '~> 3.0.7'
79 80
80 81 gem 'mail'
81 82 gem 'rdiscount'
82 83 gem 'dynamic_form'
83 84 gem 'in_place_editing'
84 85 gem 'verification', :git => 'https://github.com/sikachu/verification.git'
85 86
86 87
87 88 #---------------- testiing -----------------------
88 89 gem 'minitest-reporters'
89 90
90 91 #---------------- for console --------------------
91 92 gem 'fuzzy-string-match'
@@ -1,235 +1,247
1 1 GIT
2 2 remote: https://github.com/sikachu/verification.git
3 3 revision: ff31697b940d7b0e2ec65f08764215c96104e76d
4 4 specs:
5 5 verification (1.0.3)
6 6 actionpack (>= 3.0.0, < 5.1)
7 7 activesupport (>= 3.0.0, < 5.1)
8 8
9 9 GEM
10 10 remote: https://rubygems.org/
11 11 specs:
12 12 RubyInline (3.12.4)
13 13 ZenTest (~> 4.3)
14 14 ZenTest (4.11.1)
15 15 ace-rails-ap (4.1.1)
16 16 actionmailer (4.2.7.1)
17 17 actionpack (= 4.2.7.1)
18 18 actionview (= 4.2.7.1)
19 19 activejob (= 4.2.7.1)
20 20 mail (~> 2.5, >= 2.5.4)
21 21 rails-dom-testing (~> 1.0, >= 1.0.5)
22 22 actionpack (4.2.7.1)
23 23 actionview (= 4.2.7.1)
24 24 activesupport (= 4.2.7.1)
25 25 rack (~> 1.6)
26 26 rack-test (~> 0.6.2)
27 27 rails-dom-testing (~> 1.0, >= 1.0.5)
28 28 rails-html-sanitizer (~> 1.0, >= 1.0.2)
29 29 actionview (4.2.7.1)
30 30 activesupport (= 4.2.7.1)
31 31 builder (~> 3.1)
32 32 erubis (~> 2.7.0)
33 33 rails-dom-testing (~> 1.0, >= 1.0.5)
34 34 rails-html-sanitizer (~> 1.0, >= 1.0.2)
35 35 activejob (4.2.7.1)
36 36 activesupport (= 4.2.7.1)
37 37 globalid (>= 0.3.0)
38 38 activemodel (4.2.7.1)
39 39 activesupport (= 4.2.7.1)
40 40 builder (~> 3.1)
41 41 activerecord (4.2.7.1)
42 42 activemodel (= 4.2.7.1)
43 43 activesupport (= 4.2.7.1)
44 44 arel (~> 6.0)
45 45 activerecord-session_store (1.0.0)
46 46 actionpack (>= 4.0, < 5.1)
47 47 activerecord (>= 4.0, < 5.1)
48 48 multi_json (~> 1.11, >= 1.11.2)
49 49 rack (>= 1.5.2, < 3)
50 50 railties (>= 4.0, < 5.1)
51 51 activesupport (4.2.7.1)
52 52 i18n (~> 0.7)
53 53 json (~> 1.7, >= 1.7.7)
54 54 minitest (~> 5.1)
55 55 thread_safe (~> 0.3, >= 0.3.4)
56 56 tzinfo (~> 1.1)
57 57 ansi (1.5.0)
58 58 arel (6.0.4)
59 59 autoprefixer-rails (6.6.0)
60 60 execjs
61 61 best_in_place (3.0.3)
62 62 actionpack (>= 3.2)
63 63 railties (>= 3.2)
64 + bootstrap-datepicker-rails (1.7.1.1)
65 + railties (>= 3.0)
64 66 bootstrap-sass (3.2.0.2)
65 67 sass (~> 3.2)
66 68 bootstrap-switch-rails (3.3.3)
67 69 bootstrap-toggle-rails (2.2.1.0)
70 + bootstrap3-datetimepicker-rails (4.17.47)
71 + momentjs-rails (>= 2.8.1)
68 72 builder (3.2.2)
69 73 coffee-rails (4.2.1)
70 74 coffee-script (>= 2.2.0)
71 75 railties (>= 4.0.0, < 5.2.x)
72 76 coffee-script (2.4.1)
73 77 coffee-script-source
74 78 execjs
75 79 coffee-script-source (1.12.2)
76 80 concurrent-ruby (1.0.4)
77 81 dynamic_form (1.1.4)
78 82 erubis (2.7.0)
79 83 execjs (2.7.0)
80 84 fuzzy-string-match (1.0.0)
81 85 RubyInline (>= 3.8.6)
82 86 globalid (0.3.7)
83 87 activesupport (>= 4.1.0)
84 88 haml (4.0.7)
85 89 tilt
86 90 haml-rails (0.9.0)
87 91 actionpack (>= 4.0.1)
88 92 activesupport (>= 4.0.1)
89 93 haml (>= 4.0.6, < 5.0)
90 94 html2haml (>= 1.0.1)
91 95 railties (>= 4.0.1)
92 96 html2haml (2.0.0)
93 97 erubis (~> 2.7.0)
94 98 haml (~> 4.0.0)
95 99 nokogiri (~> 1.6.0)
96 100 ruby_parser (~> 3.5)
97 101 i18n (0.7.0)
98 102 in_place_editing (1.2.0)
99 103 jquery-countdown-rails (2.0.2)
104 + jquery-datatables-rails (3.4.0)
105 + actionpack (>= 3.1)
106 + jquery-rails
107 + railties (>= 3.1)
108 + sass-rails
100 109 jquery-rails (4.2.1)
101 110 rails-dom-testing (>= 1, < 3)
102 111 railties (>= 4.2.0)
103 112 thor (>= 0.14, < 2.0)
104 113 jquery-tablesorter (1.23.3)
105 114 railties (>= 3.2, < 6)
106 115 jquery-timepicker-addon-rails (1.4.1)
107 116 railties (>= 3.1)
108 117 jquery-ui-rails (6.0.1)
109 118 railties (>= 3.2.16)
110 119 json (1.8.3)
111 120 loofah (2.0.3)
112 121 nokogiri (>= 1.5.9)
113 122 mail (2.6.4)
114 123 mime-types (>= 1.16, < 4)
115 124 mime-types (3.1)
116 125 mime-types-data (~> 3.2015)
117 126 mime-types-data (3.2016.0521)
118 127 mini_portile2 (2.1.0)
119 128 minitest (5.10.1)
120 129 minitest-reporters (1.1.13)
121 130 ansi
122 131 builder
123 132 minitest (>= 5.0)
124 133 ruby-progressbar
125 134 momentjs-rails (2.15.1)
126 135 railties (>= 3.1)
127 136 multi_json (1.12.1)
128 137 mysql2 (0.4.5)
129 138 nokogiri (1.6.8.1)
130 139 mini_portile2 (~> 2.1.0)
131 140 rack (1.6.5)
132 141 rack-test (0.6.3)
133 142 rack (>= 1.0)
134 143 rails (4.2.7.1)
135 144 actionmailer (= 4.2.7.1)
136 145 actionpack (= 4.2.7.1)
137 146 actionview (= 4.2.7.1)
138 147 activejob (= 4.2.7.1)
139 148 activemodel (= 4.2.7.1)
140 149 activerecord (= 4.2.7.1)
141 150 activesupport (= 4.2.7.1)
142 151 bundler (>= 1.3.0, < 2.0)
143 152 railties (= 4.2.7.1)
144 153 sprockets-rails
145 154 rails-deprecated_sanitizer (1.0.3)
146 155 activesupport (>= 4.2.0.alpha)
147 156 rails-dom-testing (1.0.8)
148 157 activesupport (>= 4.2.0.beta, < 5.0)
149 158 nokogiri (~> 1.6)
150 159 rails-deprecated_sanitizer (>= 1.0.1)
151 160 rails-html-sanitizer (1.0.3)
152 161 loofah (~> 2.0)
153 162 rails_bootstrap_sortable (2.0.1)
154 163 momentjs-rails (>= 2.8.3)
155 164 railties (4.2.7.1)
156 165 actionpack (= 4.2.7.1)
157 166 activesupport (= 4.2.7.1)
158 167 rake (>= 0.8.7)
159 168 thor (>= 0.18.1, < 2.0)
160 169 rake (12.0.0)
161 170 rdiscount (2.2.0.1)
162 171 rouge (2.0.7)
163 172 ruby-progressbar (1.8.1)
164 173 ruby_parser (3.8.3)
165 174 sexp_processor (~> 4.1)
166 175 sass (3.4.23)
167 176 sass-rails (5.0.6)
168 177 railties (>= 4.0.0, < 6)
169 178 sass (~> 3.1)
170 179 sprockets (>= 2.8, < 4.0)
171 180 sprockets-rails (>= 2.0, < 4.0)
172 181 tilt (>= 1.1, < 3)
173 182 select2-rails (4.0.3)
174 183 thor (~> 0.14)
175 184 sexp_processor (4.7.0)
176 185 sprockets (3.7.1)
177 186 concurrent-ruby (~> 1.0)
178 187 rack (> 1, < 3)
179 188 sprockets-rails (3.2.0)
180 189 actionpack (>= 4.0)
181 190 activesupport (>= 4.0)
182 191 sprockets (>= 3.0.0)
183 192 sqlite3 (1.3.12)
184 193 thor (0.19.4)
185 194 thread_safe (0.3.5)
186 195 tilt (2.0.5)
187 196 tzinfo (1.2.2)
188 197 thread_safe (~> 0.1)
189 198 uglifier (3.0.4)
190 199 execjs (>= 0.3.0, < 3)
191 200 will_paginate (3.0.12)
192 201 yaml_db (0.4.2)
193 202 rails (>= 3.0, < 5.1)
194 203 rake (>= 0.8.7)
195 204
196 205 PLATFORMS
197 206 ruby
198 207
199 208 DEPENDENCIES
200 209 ace-rails-ap
201 210 activerecord-session_store
202 211 autoprefixer-rails
203 212 best_in_place (~> 3.0.1)
213 + bootstrap-datepicker-rails
204 214 bootstrap-sass (~> 3.2.0)
205 215 bootstrap-switch-rails
206 216 bootstrap-toggle-rails
217 + bootstrap3-datetimepicker-rails
207 218 coffee-rails
208 219 dynamic_form
209 220 fuzzy-string-match
210 221 haml
211 222 haml-rails
212 223 in_place_editing
213 224 jquery-countdown-rails
225 + jquery-datatables-rails
214 226 jquery-rails
215 227 jquery-tablesorter
216 228 jquery-timepicker-addon-rails
217 229 jquery-ui-rails
218 230 mail
219 231 minitest-reporters
220 232 momentjs-rails
221 233 mysql2
222 234 rails (~> 4.2.0)
223 235 rails_bootstrap_sortable
224 236 rdiscount
225 237 rouge
226 238 sass-rails
227 239 select2-rails
228 240 sqlite3
229 241 uglifier
230 242 verification!
231 243 will_paginate (~> 3.0.7)
232 244 yaml_db
233 245
234 246 BUNDLED WITH
235 - 1.13.6
247 + 1.15.4
@@ -1,41 +1,47
1 1 // This is a manifest file that'll be compiled into application.js, which will include all the files
2 2 // listed below.
3 3 //
4 4 // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 5 // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6 6 //
7 7 // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 8 // the compiled file.
9 9 //
10 10 // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11 11 // GO AFTER THE REQUIRES BELOW.
12 12 //
13 13 //= require jquery
14 14 //= require jquery_ujs
15 + //= require dataTables/jquery.dataTables
16 + //= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
15 17 //= require jquery-ui
16 18 //= require bootstrap-sprockets
17 19 //= require moment
20 + //= require moment/th
18 21 //= require bootstrap-sortable
22 + //= require bootstrap-datetimepicker
19 23 //= require select2
20 24 //= require ace-rails-ap
21 25 //= require ace/mode-c_cpp
22 26 //= require ace/mode-python
23 27 //= require ace/mode-ruby
24 28 //= require ace/mode-pascal
25 29 //= require ace/mode-javascript
26 30 //= require ace/mode-java
27 31 //= require ace/theme-merbivore
28 32 //= require custom
29 33 //= require jquery.countdown
30 34 //-------------- addition from local_jquery -----------
31 35 //= require jquery-tablesorter
32 36 //= require best_in_place
33 37 //= require best_in_place.jquery-ui
34 38 //= require brython
39 + //= require bootstrap-datepicker
40 + //= require bootstrap-datetimepicker
35 41
36 42 // since this is after blank line, it is not downloaded
37 43 //x= require prototype
38 44 //x= require prototype_ujs
39 45 //x= require effects
40 46 //x= require dragdrop
41 47 //x= require controls
@@ -1,554 +1,557
1 1 /* This is a manifest file that'll be compiled into application.css, which will include all the files
2 2 * listed below.
3 3 *
4 4 * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
5 5 * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
6 6 *
7 7 * You're free to add application-wide styles to this file and they'll appear at the bottom of the
8 8 * compiled file so the styles you add here take precedence over styles defined in any styles
9 9 * defined in the other CSS/SCSS files in this directory. It is generally better to create a new
10 10 * file per style scope.
11 11 *
12 12 * // bootstrap says that we should not do this, but @import each file instead
13 13 * # *= require_tree .
14 14 * # *= require_self
15 15 */
16 16
17 17 @import "jquery-ui";
18 18 //@import "jquery.ui.core";
19 19 //@import "jquery.ui.theme";
20 20 //@import "jquery.ui.datepicker";
21 21 //@import "jquery.ui.slider";
22 22 @import "jquery-ui-timepicker-addon";
23 23 @import "jquery-tablesorter/theme.metro-dark";
24 24 @import "jquery.countdown";
25 25 @import "tablesorter-theme.cafe";
26 26
27 27 //bootstrap
28 28 @import "bootstrap-sprockets";
29 29 @import "bootstrap";
30 30 @import "select2";
31 31 @import "select2-bootstrap";
32 32
33 33 //@import bootstrap3-switch
34 34 @import "bootstrap-toggle";
35 35 @import "bootstrap-sortable";
36 + @import "bootstrap-datepicker3";
37 + @import "bootstrap-datetimepicker";
38 + @import "dataTables/bootstrap/3/jquery.dataTables.bootstrap";
36 39
37 40 //bootstrap navbar color (from)
38 41 $bgDefault: #19197b;
39 42 $bgHighlight: #06064b;
40 43 $colDefault: #8e8eb4;
41 44 $colHighlight: #ffffff;
42 45 $dropDown: false;
43 46
44 47 @font-face {
45 48 font-family: 'Glyphicons Halflings';
46 49 src: font-path('bootstrap/glyphicons-halflings-regular.eot');
47 50 src: font-path('bootstrap/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
48 51 font-path('bootstrap/glyphicons-halflings-regular.woff') format('woff'),
49 52 font-path('bootstrap/glyphicons-halflings-regular.ttf') format('truetype'),
50 53 font-path('bootstrap/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');
51 54 }
52 55
53 56
54 57 .navbar-default {
55 58 background-color: $bgDefault;
56 59 border-color: $bgHighlight;
57 60
58 61 .navbar-brand {
59 62 color: $colDefault;
60 63
61 64 &:hover, &:focus {
62 65 color: $colHighlight;
63 66 }
64 67 }
65 68
66 69 .navbar-text {
67 70 color: $colDefault;
68 71 }
69 72
70 73 .navbar-nav {
71 74 > li {
72 75 > a {
73 76 color: $colDefault;
74 77
75 78 &:hover, &:focus {
76 79 color: $colHighlight;
77 80 }
78 81 }
79 82
80 83 @if $dropDown {
81 84 > .dropdown-menu {
82 85 background-color: $bgDefault;
83 86
84 87 > li {
85 88 > a {
86 89 color: $colDefault;
87 90
88 91 &:hover, &:focus {
89 92 color: $colHighlight;
90 93 background-color: $bgHighlight;
91 94 }
92 95 }
93 96
94 97 > .divider {
95 98 background-color: $bgHighlight;
96 99 }
97 100 }
98 101 }
99 102 }
100 103 }
101 104
102 105 @if $dropDown {
103 106 .open .dropdown-menu > .active {
104 107 > a, > a:hover, > a:focus {
105 108 color: $colHighlight;
106 109 background-color: $bgHighlight;
107 110 }
108 111 }
109 112 }
110 113
111 114 > .active {
112 115 > a, > a:hover, > a:focus {
113 116 color: $colHighlight;
114 117 background-color: $bgHighlight;
115 118 }
116 119 }
117 120
118 121 > .open {
119 122 > a, > a:hover, > a:focus {
120 123 color: $colHighlight;
121 124 background-color: $bgHighlight;
122 125 }
123 126 }
124 127 }
125 128
126 129 .navbar-toggle {
127 130 border-color: $bgHighlight;
128 131
129 132 &:hover, &:focus {
130 133 background-color: $bgHighlight;
131 134 }
132 135
133 136 .icon-bar {
134 137 background-color: $colDefault;
135 138 }
136 139 }
137 140
138 141 .navbar-collapse,
139 142 .navbar-form {
140 143 border-color: $colDefault;
141 144 }
142 145
143 146 .navbar-link {
144 147 color: $colDefault;
145 148
146 149 &:hover {
147 150 color: $colHighlight;
148 151 }
149 152 }
150 153 }
151 154
152 155 @media (max-width: 767px) {
153 156 .navbar-default .navbar-nav .open .dropdown-menu {
154 157 > li > a {
155 158 color: $colDefault;
156 159
157 160 &:hover, &:focus {
158 161 color: $colHighlight;
159 162 }
160 163 }
161 164
162 165 > .active {
163 166 > a, > a:hover, > a:focus {
164 167 color: $colHighlight;
165 168 background-color: $bgHighlight;
166 169 }
167 170 }
168 171 }
169 172 }
170 173
171 174 .secondnavbar {
172 175 top: 50px;
173 176 }
174 177
175 178 // --------------- bootstrap file upload ----------------------
176 179 .btn-file {
177 180 position: relative;
178 181 overflow: hidden;
179 182 }
180 183
181 184 .btn-file input[type=file] {
182 185 position: absolute;
183 186 top: 0;
184 187 right: 0;
185 188 min-width: 100%;
186 189 min-height: 100%;
187 190 font-size: 100px;
188 191 text-align: right;
189 192 filter: alpha(opacity = 0);
190 193 opacity: 0;
191 194 outline: none;
192 195 background: white;
193 196 cursor: inherit;
194 197 display: block;
195 198 }
196 199
197 200 body {
198 201 background: white image-url("topbg.jpg") repeat-x top center;
199 202
200 203 //font-size: 13px
201 204 //font-family: Tahoma, "sans-serif"
202 205 margin: 10px;
203 206 padding: 10px;
204 207 padding-top: 60px;
205 208 }
206 209
207 210 // ------------------ bootstrap sortable --------------------
208 211 table.sortable th {
209 212 padding-right: 20px !important;
210 213
211 214 span.sign {
212 215 right: (-15px) !important;
213 216 }
214 217
215 218 &.text-right {
216 219 padding-left: 20px !important;
217 220 padding-right: 8px !important;
218 221
219 222 &:after, span.sign {
220 223 left: (-15px) !important;
221 224 }
222 225 }
223 226 }
224 227
225 228 input {
226 229 font-family: Tahoma, "sans-serif";
227 230 }
228 231
229 232 h1 {
230 233 font-size: 24px;
231 234 color: #334488;
232 235 line-height: 2em;
233 236 }
234 237
235 238 h2 {
236 239 font-size: 18px;
237 240 color: #5566bb;
238 241 line-height: 1.5em;
239 242 }
240 243
241 244 hr {
242 245 border-top: 1px solid #dddddd;
243 246 border-bottom: 1px solid #eeeeee;
244 247 }
245 248
246 249 //#a
247 250 // color: #6666cc
248 251 // text-decoration: none
249 252 //
250 253 // &:link, &:visited
251 254 // color: #6666cc
252 255 // text-decoration: none
253 256 //
254 257 // &:hover, &:focus
255 258 // color: #111166
256 259 // text-decoration: none
257 260
258 261 div {
259 262 &.userbar {
260 263 line-height: 1.5em;
261 264 text-align: right;
262 265 font-size: 12px;
263 266 }
264 267
265 268 &.title {
266 269 padding: 10px 0px;
267 270 line-height: 1.5em;
268 271 font-size: 13px;
269 272
270 273 span.contest-over-msg {
271 274 font-size: 15px;
272 275 color: red;
273 276 }
274 277
275 278 table {
276 279 width: 100%;
277 280 font-weight: bold;
278 281 }
279 282
280 283 td {
281 284 &.left-col {
282 285 text-align: left;
283 286 vertical-align: top;
284 287 color: #444444;
285 288 }
286 289
287 290 &.right-col {
288 291 text-align: right;
289 292 vertical-align: top;
290 293 font-size: 18px;
291 294 color: #116699;
292 295 }
293 296 }
294 297 }
295 298 }
296 299
297 300 table.info {
298 301 margin: 10px 0;
299 302 border: 1px solid #666666;
300 303 border-collapse: collapse;
301 304 font-size: 12px;
302 305
303 306 th {
304 307 border: 1px solid #666666;
305 308 line-height: 1.5em;
306 309 padding: 0 0.5em;
307 310 }
308 311
309 312 td {
310 313 border-left: 1px solid #666666;
311 314 border-right: 1px solid #666666;
312 315 line-height: 1.5em;
313 316 padding: 0 0.5em;
314 317 }
315 318 }
316 319
317 320 tr {
318 321 &.info-head {
319 322 background: #777777;
320 323 color: white;
321 324 }
322 325
323 326 &.info-odd {
324 327 background: #eeeeee;
325 328 }
326 329
327 330 &.info-even {
328 331 background: #fcfcfc;
329 332 }
330 333 }
331 334
332 335 @mixin basicbox {
333 336 background: #eeeeff;
334 337 border: 1px dotted #99aaee;
335 338 padding: 5px;
336 339 margin: 10px 0px;
337 340 color: black;
338 341 font-size: 13px;
339 342 }
340 343
341 344 .infobox {
342 345 @include basicbox;
343 346 }
344 347
345 348 .submitbox {
346 349 @include basicbox;
347 350 }
348 351
349 352 .errorExplanation {
350 353 border: 1px dotted gray;
351 354 color: #bb2222;
352 355 padding: 5px 15px 5px 15px;
353 356 margin-bottom: 5px;
354 357 background-color: white;
355 358 font-weight: normal;
356 359
357 360 h2 {
358 361 color: #cc1111;
359 362 font-weight: bold;
360 363 }
361 364 }
362 365
363 366 table.uinfo {
364 367 border-collapse: collapse;
365 368 border: 1px solid black;
366 369 font-size: 13px;
367 370 }
368 371
369 372 td.uinfo {
370 373 vertical-align: top;
371 374 border: 1px solid black;
372 375 padding: 5px;
373 376 }
374 377
375 378 th.uinfo {
376 379 background: lightgreen;
377 380 vertical-align: top;
378 381 text-align: right;
379 382 border: 1px solid black;
380 383 padding: 5px;
381 384 }
382 385
383 386 div {
384 387 &.compilermsgbody {
385 388 font-family: monospace;
386 389 }
387 390
388 391 &.task-menu {
389 392 text-align: center;
390 393 font-size: 13px;
391 394 line-height: 1.75em;
392 395 font-weight: bold;
393 396 border-top: 1px dashed gray;
394 397 border-bottom: 1px dashed gray;
395 398 margin-top: 2px;
396 399 margin-bottom: 4px;
397 400 }
398 401 }
399 402
400 403 table.taskdesc {
401 404 border: 2px solid #dddddd;
402 405 border-collapse: collapse;
403 406 margin: 10px auto;
404 407 width: 90%;
405 408 font-size: 13px;
406 409
407 410 p {
408 411 font-size: 13px;
409 412 }
410 413
411 414 tr.name {
412 415 border: 2px solid #dddddd;
413 416 background: #dddddd;
414 417 color: #333333;
415 418 font-weight: bold;
416 419 font-size: 14px;
417 420 line-height: 1.5em;
418 421 text-align: center;
419 422 }
420 423
421 424 td {
422 425 &.desc-odd {
423 426 padding: 5px;
424 427 padding-left: 20px;
425 428 background: #fefeee;
426 429 }
427 430
428 431 &.desc-even {
429 432 padding: 5px;
430 433 padding-left: 20px;
431 434 background: #feeefe;
432 435 }
433 436 }
434 437 }
435 438
436 439 .announcementbox {
437 440 margin: 10px 0px;
438 441 background: #bbddee;
439 442 padding: 1px;
440 443
441 444 span.title {
442 445 font-weight: bold;
443 446 color: #224455;
444 447 padding-left: 10px;
445 448 line-height: 1.6em;
446 449 }
447 450 }
448 451
449 452 .announcement {
450 453 margin: 2px;
451 454 background: white;
452 455 padding: 1px;
453 456 padding-left: 10px;
454 457 padding-right: 10px;
455 458 padding-top: 5px;
456 459 padding-bottom: 5px;
457 460 }
458 461
459 462 .announcement p {
460 463 font-size: 12px;
461 464 margin: 2px;
462 465 }
463 466
464 467 .pub-info {
465 468 text-align: right;
466 469 font-style: italic;
467 470 font-size: 9px;
468 471
469 472 p {
470 473 text-align: right;
471 474 font-style: italic;
472 475 font-size: 9px;
473 476 }
474 477 }
475 478
476 479 .announcement {
477 480 .toggles {
478 481 font-weight: normal;
479 482 float: right;
480 483 font-size: 80%;
481 484 }
482 485
483 486 .announcement-title {
484 487 font-weight: bold;
485 488 }
486 489 }
487 490
488 491 div {
489 492 &.message {
490 493 margin: 10px 0 0;
491 494
492 495 div {
493 496 &.message {
494 497 margin: 0 0 0 30px;
495 498 }
496 499
497 500 &.body {
498 501 border: 2px solid #dddddd;
499 502 background: #fff8f8;
500 503 padding-left: 5px;
501 504 }
502 505
503 506 &.reply-body {
504 507 border: 2px solid #bbbbbb;
505 508 background: #fffff8;
506 509 padding-left: 5px;
507 510 }
508 511
509 512 &.stat {
510 513 font-size: 10px;
511 514 line-height: 1.75em;
512 515 padding: 0 5px;
513 516 color: #333333;
514 517 background: #dddddd;
515 518 font-weight: bold;
516 519 }
517 520
518 521 &.message div.stat {
519 522 font-size: 10px;
520 523 line-height: 1.75em;
521 524 padding: 0 5px;
522 525 color: #444444;
523 526 background: #bbbbbb;
524 527 font-weight: bold;
525 528 }
526 529 }
527 530 }
528 531
529 532 &.contest-title {
530 533 color: white;
531 534 text-align: center;
532 535 line-height: 2em;
533 536 }
534 537
535 538 &.registration-desc, &.test-desc {
536 539 border: 1px dotted gray;
537 540 background: #f5f5f5;
538 541 padding: 5px;
539 542 margin: 10px 0;
540 543 font-size: 12px;
541 544 line-height: 1.5em;
542 545 }
543 546 }
544 547
545 548 h2.contest-title {
546 549 margin-top: 5px;
547 550 margin-bottom: 5px;
548 551 }
549 552
550 553
551 554
552 555 .grader-comment {
553 556 word-wrap: break-word;
554 557 }
@@ -1,57 +1,63
1 1 class LoginController < ApplicationController
2 2
3 3 def index
4 4 # show login screen
5 5 reset_session
6 6 redirect_to :controller => 'main', :action => 'login'
7 7 end
8 8
9 9 def login
10 - if (!GraderConfiguration['right.bypass_agreement']) and (!params[:accept_agree])
10 + user = User.authenticate(params[:login], params[:password])
11 + unless user
12 + flash[:notice] = 'Wrong password'
13 + redirect_to :controller => 'main', :action => 'login'
14 + return
15 + end
16 +
17 + if (!GraderConfiguration['right.bypass_agreement']) and (!params[:accept_agree]) and !user.admin?
11 18 flash[:notice] = 'You must accept the agreement before logging in'
12 19 redirect_to :controller => 'main', :action => 'login'
13 - elsif user = User.authenticate(params[:login], params[:password])
14 - session[:user_id] = user.id
15 - session[:admin] = user.admin?
20 + return
21 + end
22 +
23 + #process logging in
24 + session[:user_id] = user.id
25 + session[:admin] = user.admin?
16 26
17 - # clear forced logout flag for multicontests contest change
18 - if GraderConfiguration.multicontests?
19 - contest_stat = user.contest_stat
20 - if contest_stat.respond_to? :forced_logout
21 - if contest_stat.forced_logout
22 - contest_stat.forced_logout = false
23 - contest_stat.save
24 - end
27 + # clear forced logout flag for multicontests contest change
28 + if GraderConfiguration.multicontests?
29 + contest_stat = user.contest_stat
30 + if contest_stat.respond_to? :forced_logout
31 + if contest_stat.forced_logout
32 + contest_stat.forced_logout = false
33 + contest_stat.save
25 34 end
26 35 end
27 -
28 - #save login information
29 - Login.create(user_id: user.id, ip_address: request.remote_ip)
36 + end
30 37
31 - redirect_to :controller => 'main', :action => 'list'
32 - else
33 - flash[:notice] = 'Wrong password'
34 - redirect_to :controller => 'main', :action => 'login'
35 - end
38 + #save login information
39 + Login.create(user_id: user.id, ip_address: request.remote_ip)
40 +
41 + redirect_to :controller => 'main', :action => 'list'
36 42 end
37 43
38 44 def site_login
39 45 begin
40 46 site = Site.find(params[:login][:site_id])
41 47 rescue ActiveRecord::RecordNotFound
42 48 site = nil
43 49 end
44 50 if site==nil
45 51 flash[:notice] = 'Wrong site'
46 52 redirect_to :controller => 'main', :action => 'login' and return
47 53 end
48 54 if (site.password) and (site.password == params[:login][:password])
49 55 session[:site_id] = site.id
50 56 redirect_to :controller => 'site', :action => 'index'
51 57 else
52 58 flash[:notice] = 'Wrong site password'
53 59 redirect_to :controller => 'site', :action => 'login'
54 60 end
55 61 end
56 62
57 63 end
@@ -1,309 +1,306
1 1 class ProblemsController < ApplicationController
2 2
3 3 before_action :authenticate, :authorization
4 4 before_action :testcase_authorization, only: [:show_testcase]
5 5
6 6 in_place_edit_for :problem, :name
7 7 in_place_edit_for :problem, :full_name
8 8 in_place_edit_for :problem, :full_score
9 9
10 10 def index
11 11 @problems = Problem.order(date_added: :desc)
12 12 end
13 13
14 14 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
15 15 verify :method => :post, :only => [ :create, :quick_create,
16 16 :do_manage,
17 17 :do_import,
18 18 ],
19 19 :redirect_to => { :action => :index }
20 20
21 21 def show
22 22 @problem = Problem.find(params[:id])
23 23 end
24 24
25 25 def new
26 26 @problem = Problem.new
27 27 @description = nil
28 28 end
29 29
30 30 def create
31 31 @problem = Problem.new(problem_params)
32 32 @description = Description.new(params[:description])
33 33 if @description.body!=''
34 34 if !@description.save
35 35 render :action => new and return
36 36 end
37 37 else
38 38 @description = nil
39 39 end
40 40 @problem.description = @description
41 41 if @problem.save
42 42 flash[:notice] = 'Problem was successfully created.'
43 43 redirect_to action: :index
44 44 else
45 45 render :action => 'new'
46 46 end
47 47 end
48 48
49 49 def quick_create
50 50 @problem = Problem.new(problem_params)
51 51 @problem.full_name = @problem.name if @problem.full_name == ''
52 52 @problem.full_score = 100
53 53 @problem.available = false
54 54 @problem.test_allowed = true
55 55 @problem.output_only = false
56 56 @problem.date_added = Time.new
57 57 if @problem.save
58 58 flash[:notice] = 'Problem was successfully created.'
59 59 redirect_to action: :index
60 60 else
61 61 flash[:notice] = 'Error saving problem'
62 62 redirect_to action: :index
63 63 end
64 64 end
65 65
66 66 def edit
67 67 @problem = Problem.find(params[:id])
68 68 @description = @problem.description
69 69 end
70 70
71 71 def update
72 72 @problem = Problem.find(params[:id])
73 73 @description = @problem.description
74 74 if @description.nil? and params[:description][:body]!=''
75 75 @description = Description.new(params[:description])
76 76 if !@description.save
77 77 flash[:notice] = 'Error saving description'
78 78 render :action => 'edit' and return
79 79 end
80 80 @problem.description = @description
81 81 elsif @description
82 82 if !@description.update_attributes(params[:description])
83 83 flash[:notice] = 'Error saving description'
84 84 render :action => 'edit' and return
85 85 end
86 86 end
87 87 if params[:file] and params[:file].content_type != 'application/pdf'
88 88 flash[:notice] = 'Error: Uploaded file is not PDF'
89 89 render :action => 'edit' and return
90 90 end
91 91 if @problem.update_attributes(problem_params)
92 92 flash[:notice] = 'Problem was successfully updated.'
93 93 unless params[:file] == nil or params[:file] == ''
94 94 flash[:notice] = 'Problem was successfully updated and a new PDF file is uploaded.'
95 95 out_dirname = "#{Problem.download_file_basedir}/#{@problem.id}"
96 96 if not FileTest.exists? out_dirname
97 97 Dir.mkdir out_dirname
98 98 end
99 99
100 100 out_filename = "#{out_dirname}/#{@problem.name}.pdf"
101 101 if FileTest.exists? out_filename
102 102 File.delete out_filename
103 103 end
104 104
105 105 File.open(out_filename,"wb") do |file|
106 106 file.write(params[:file].read)
107 107 end
108 108 @problem.description_filename = "#{@problem.name}.pdf"
109 109 @problem.save
110 110 end
111 111 redirect_to :action => 'show', :id => @problem
112 112 else
113 113 render :action => 'edit'
114 114 end
115 115 end
116 116
117 117 def destroy
118 118 p = Problem.find(params[:id]).destroy
119 119 redirect_to action: :index
120 120 end
121 121
122 122 def toggle
123 123 @problem = Problem.find(params[:id])
124 124 @problem.update_attributes(available: !(@problem.available) )
125 125 respond_to do |format|
126 126 format.js { }
127 127 end
128 128 end
129 129
130 130 def toggle_test
131 131 @problem = Problem.find(params[:id])
132 132 @problem.update_attributes(test_allowed: !(@problem.test_allowed?) )
133 133 respond_to do |format|
134 134 format.js { }
135 135 end
136 136 end
137 137
138 138 def toggle_view_testcase
139 139 @problem = Problem.find(params[:id])
140 140 @problem.update_attributes(view_testcase: !(@problem.view_testcase?) )
141 141 respond_to do |format|
142 142 format.js { }
143 143 end
144 144 end
145 145
146 146 def turn_all_off
147 147 Problem.available.all.each do |problem|
148 148 problem.available = false
149 149 problem.save
150 150 end
151 151 redirect_to action: :index
152 152 end
153 153
154 154 def turn_all_on
155 155 Problem.where.not(available: true).each do |problem|
156 156 problem.available = true
157 157 problem.save
158 158 end
159 159 redirect_to action: :index
160 160 end
161 161
162 162 def stat
163 163 @problem = Problem.find(params[:id])
164 164 unless @problem.available or session[:admin]
165 165 redirect_to :controller => 'main', :action => 'list'
166 166 return
167 167 end
168 168 @submissions = Submission.includes(:user).where(problem_id: params[:id]).order(:user_id,:id)
169 169
170 170 #stat summary
171 171 range =65
172 172 @histogram = { data: Array.new(range,0), summary: {} }
173 173 user = Hash.new(0)
174 174 @submissions.find_each do |sub|
175 175 d = (DateTime.now.in_time_zone - sub.submitted_at) / 24 / 60 / 60
176 176 @histogram[:data][d.to_i] += 1 if d < range
177 177 user[sub.user_id] = [user[sub.user_id], ((sub.try(:points) || 0) >= @problem.full_score) ? 1 : 0].max
178 178 end
179 179 @histogram[:summary][:max] = [@histogram[:data].max,1].max
180 180
181 181 @summary = { attempt: user.count, solve: 0 }
182 182 user.each_value { |v| @summary[:solve] += 1 if v == 1 }
183 183 end
184 184
185 185 def manage
186 186 @problems = Problem.order(date_added: :desc)
187 187 end
188 188
189 189 def do_manage
190 190 if params.has_key? 'change_date_added'
191 191 change_date_added
192 192 elsif params.has_key? 'add_to_contest'
193 193 add_to_contest
194 194 elsif params.has_key? 'enable_problem'
195 195 set_available(true)
196 196 elsif params.has_key? 'disable_problem'
197 197 set_available(false)
198 198 elsif params.has_key? 'add_group'
199 199 group = Group.find(params[:group_id])
200 200 ok = []
201 201 failed = []
202 202 get_problems_from_params.each do |p|
203 203 begin
204 204 group.problems << p
205 205 ok << p.full_name
206 206 rescue => e
207 207 failed << p.full_name
208 208 end
209 209 end
210 210 flash[:success] = "The following problems are added to the group #{group.name}: " + ok.join(', ') if ok.count > 0
211 211 flash[:alert] = "The following problems are already in the group #{group.name}: " + failed.join(', ') if failed.count > 0
212 212 end
213 213
214 214 redirect_to :action => 'manage'
215 215 end
216 216
217 217 def import
218 218 @allow_test_pair_import = allow_test_pair_import?
219 219 end
220 220
221 221 def do_import
222 222 old_problem = Problem.find_by_name(params[:name])
223 223 if !allow_test_pair_import? and params.has_key? :import_to_db
224 224 params.delete :import_to_db
225 225 end
226 226 @problem, import_log = Problem.create_from_import_form_params(params,
227 227 old_problem)
228 228
229 229 if !@problem.errors.empty?
230 230 render :action => 'import' and return
231 231 end
232 232
233 233 if old_problem!=nil
234 234 flash[:notice] = "The test data has been replaced for problem #{@problem.name}"
235 235 end
236 236 @log = import_log
237 237 end
238 238
239 239 def remove_contest
240 240 problem = Problem.find(params[:id])
241 241 contest = Contest.find(params[:contest_id])
242 242 if problem!=nil and contest!=nil
243 243 problem.contests.delete(contest)
244 244 end
245 245 redirect_to :action => 'manage'
246 246 end
247 247
248 248 ##################################
249 249 protected
250 250
251 251 def allow_test_pair_import?
252 252 if defined? ALLOW_TEST_PAIR_IMPORT
253 253 return ALLOW_TEST_PAIR_IMPORT
254 254 else
255 255 return false
256 256 end
257 257 end
258 258
259 259 def change_date_added
260 260 problems = get_problems_from_params
261 - year = params[:date_added][:year].to_i
262 - month = params[:date_added][:month].to_i
263 - day = params[:date_added][:day].to_i
264 - date = Date.new(year,month,day)
261 + date = Date.parse(params[:date_added])
265 262 problems.each do |p|
266 263 p.date_added = date
267 264 p.save
268 265 end
269 266 end
270 267
271 268 def add_to_contest
272 269 problems = get_problems_from_params
273 270 contest = Contest.find(params[:contest][:id])
274 271 if contest!=nil and contest.enabled
275 272 problems.each do |p|
276 273 p.contests << contest
277 274 end
278 275 end
279 276 end
280 277
281 278 def set_available(avail)
282 279 problems = get_problems_from_params
283 280 problems.each do |p|
284 281 p.available = avail
285 282 p.save
286 283 end
287 284 end
288 285
289 286 def get_problems_from_params
290 287 problems = []
291 288 params.keys.each do |k|
292 289 if k.index('prob-')==0
293 290 name, id, order = k.split('-')
294 291 problems << Problem.find(id)
295 292 end
296 293 end
297 294 problems
298 295 end
299 296
300 297 def get_problems_stat
301 298 end
302 299
303 300 private
304 301
305 302 def problem_params
306 303 params.require(:problem).permit(:name, :full_name, :full_score, :date_added, :available, :test_allowed,:output_only, :url, :description)
307 304 end
308 305
309 306 end
@@ -1,606 +1,607
1 1 require 'csv'
2 2
3 3 class UserAdminController < ApplicationController
4 4
5 5 include MailHelperMethods
6 6
7 7 before_filter :admin_authorization
8 8
9 9 # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html)
10 10 verify :method => :post, :only => [
11 11 :create, :create_from_list,
12 12 :update,
13 13 :manage_contest,
14 14 :bulk_mail
15 15 ],
16 16 :redirect_to => { :action => :list }
17 17
18 18 def index
19 19 @user_count = User.count
20 20 if params[:page] == 'all'
21 21 @users = User.all
22 22 @paginated = false
23 23 else
24 24 @users = User.paginate :page => params[:page]
25 25 @paginated = true
26 26 end
27 + @users = User.all
27 28 @hidden_columns = ['hashed_password', 'salt', 'created_at', 'updated_at']
28 29 @contests = Contest.enabled
29 30 end
30 31
31 32 def active
32 33 sessions = ActiveRecord::SessionStore::Session.where("updated_at >= ?", 60.minutes.ago)
33 34 @users = []
34 35 sessions.each do |session|
35 36 if session.data[:user_id]
36 37 @users << User.find(session.data[:user_id])
37 38 end
38 39 end
39 40 end
40 41
41 42 def show
42 43 @user = User.find(params[:id])
43 44 end
44 45
45 46 def new
46 47 @user = User.new
47 48 end
48 49
49 50 def create
50 51 @user = User.new(user_params)
51 52 @user.activated = true
52 53 if @user.save
53 54 flash[:notice] = 'User was successfully created.'
54 55 redirect_to :action => 'index'
55 56 else
56 57 render :action => 'new'
57 58 end
58 59 end
59 60
60 61 def clear_last_ip
61 62 @user = User.find(params[:id])
62 63 @user.last_ip = nil
63 64 @user.save
64 65 redirect_to action: 'index', page: params[:page]
65 66 end
66 67
67 68 def create_from_list
68 69 lines = params[:user_list]
69 70
70 71 note = []
71 72
72 73 lines.split("\n").each do |line|
73 74 items = line.chomp.split(',')
74 75 if items.length>=2
75 76 login = items[0]
76 77 full_name = items[1]
77 78 remark =''
78 79 user_alias = ''
79 80
80 81 added_random_password = false
81 82 if items.length >= 3 and items[2].chomp(" ").length > 0;
82 83 password = items[2].chomp(" ")
83 84 else
84 85 password = random_password
85 86 add_random_password=true;
86 87 end
87 88
88 89 if items.length>= 4 and items[3].chomp(" ").length > 0;
89 90 user_alias = items[3].chomp(" ")
90 91 else
91 92 user_alias = login
92 93 end
93 94
94 95 if items.length>=5
95 96 remark = items[4].strip;
96 97 end
97 98
98 99 user = User.find_by_login(login)
99 100 if (user)
100 101 user.full_name = full_name
101 102 user.password = password
102 103 user.remark = remark
103 104 else
104 105 user = User.new({:login => login,
105 106 :full_name => full_name,
106 107 :password => password,
107 108 :password_confirmation => password,
108 109 :alias => user_alias,
109 110 :remark => remark})
110 111 end
111 112 user.activated = true
112 113 user.save
113 114
114 115 if added_random_password
115 116 note << "'#{login}' (+)"
116 117 else
117 118 note << login
118 119 end
119 120 end
120 121 end
121 122 flash[:success] = 'User(s) ' + note.join(', ') +
122 123 ' were successfully created. ' +
123 124 '( (+) - created with random passwords.)'
124 125 redirect_to :action => 'index'
125 126 end
126 127
127 128 def edit
128 129 @user = User.find(params[:id])
129 130 end
130 131
131 132 def update
132 133 @user = User.find(params[:id])
133 134 if @user.update_attributes(user_params)
134 135 flash[:notice] = 'User was successfully updated.'
135 136 redirect_to :action => 'show', :id => @user
136 137 else
137 138 render :action => 'edit'
138 139 end
139 140 end
140 141
141 142 def destroy
142 143 User.find(params[:id]).destroy
143 144 redirect_to :action => 'index'
144 145 end
145 146
146 147 def user_stat
147 148 if params[:commit] == 'download csv'
148 149 @problems = Problem.all
149 150 else
150 151 @problems = Problem.available_problems
151 152 end
152 153 @users = User.includes(:contests, :contest_stat).where(enabled: true)
153 154 @scorearray = Array.new
154 155 @users.each do |u|
155 156 ustat = Array.new
156 157 ustat[0] = u
157 158 @problems.each do |p|
158 159 sub = Submission.find_last_by_user_and_problem(u.id,p.id)
159 160 if (sub!=nil) and (sub.points!=nil) and p and p.full_score
160 161 ustat << [(sub.points.to_f*100/p.full_score).round, (sub.points>=p.full_score)]
161 162 else
162 163 ustat << [0,false]
163 164 end
164 165 end
165 166 @scorearray << ustat
166 167 end
167 168 if params[:commit] == 'download csv' then
168 169 csv = gen_csv_from_scorearray(@scorearray,@problems)
169 170 send_data csv, filename: 'last_score.csv'
170 171 else
171 172 render template: 'user_admin/user_stat'
172 173 end
173 174 end
174 175
175 176 def user_stat_max
176 177 if params[:commit] == 'download csv'
177 178 @problems = Problem.all
178 179 else
179 180 @problems = Problem.available_problems
180 181 end
181 182 @users = User.includes(:contests).includes(:contest_stat).all
182 183 @scorearray = Array.new
183 184 #set up range from param
184 185 since_id = params.fetch(:since_id, 0).to_i
185 186 until_id = params.fetch(:until_id, 0).to_i
186 187 @users.each do |u|
187 188 ustat = Array.new
188 189 ustat[0] = u
189 190 @problems.each do |p|
190 191 max_points = 0
191 192 Submission.find_in_range_by_user_and_problem(u.id,p.id,since_id,until_id).each do |sub|
192 193 max_points = sub.points if sub and sub.points and (sub.points > max_points)
193 194 end
194 195 ustat << [(max_points.to_f*100/p.full_score).round, (max_points>=p.full_score)]
195 196 end
196 197 @scorearray << ustat
197 198 end
198 199
199 200 if params[:commit] == 'download csv' then
200 201 csv = gen_csv_from_scorearray(@scorearray,@problems)
201 202 send_data csv, filename: 'max_score.csv'
202 203 else
203 204 render template: 'user_admin/user_stat'
204 205 end
205 206 end
206 207
207 208 def import
208 209 if params[:file]==''
209 210 flash[:notice] = 'Error importing no file'
210 211 redirect_to :action => 'index' and return
211 212 end
212 213 import_from_file(params[:file])
213 214 end
214 215
215 216 def random_all_passwords
216 217 users = User.all
217 218 @prefix = params[:prefix] || ''
218 219 @non_admin_users = User.find_non_admin_with_prefix(@prefix)
219 220 @changed = false
220 221 if request.request_method == 'POST'
221 222 @non_admin_users.each do |user|
222 223 password = random_password
223 224 user.password = password
224 225 user.password_confirmation = password
225 226 user.save
226 227 end
227 228 @changed = true
228 229 end
229 230 end
230 231
231 232
232 233 # contest management
233 234
234 235 def contests
235 236 @contest, @users = find_contest_and_user_from_contest_id(params[:id])
236 237 @contests = Contest.enabled
237 238 end
238 239
239 240 def assign_from_list
240 241 contest_id = params[:users_contest_id]
241 242 org_contest, users = find_contest_and_user_from_contest_id(contest_id)
242 243 contest = Contest.find(params[:new_contest][:id])
243 244 if !contest
244 245 flash[:notice] = 'Error: no contest'
245 246 redirect_to :action => 'contests', :id =>contest_id
246 247 end
247 248
248 249 note = []
249 250 users.each do |u|
250 251 u.contests = [contest]
251 252 note << u.login
252 253 end
253 254 flash[:notice] = 'User(s) ' + note.join(', ') +
254 255 " were successfully reassigned to #{contest.title}."
255 256 redirect_to :action => 'contests', :id =>contest.id
256 257 end
257 258
258 259 def add_to_contest
259 260 user = User.find(params[:id])
260 261 contest = Contest.find(params[:contest_id])
261 262 if user and contest
262 263 user.contests << contest
263 264 end
264 265 redirect_to :action => 'index'
265 266 end
266 267
267 268 def remove_from_contest
268 269 user = User.find(params[:id])
269 270 contest = Contest.find(params[:contest_id])
270 271 if user and contest
271 272 user.contests.delete(contest)
272 273 end
273 274 redirect_to :action => 'index'
274 275 end
275 276
276 277 def contest_management
277 278 end
278 279
279 280 def manage_contest
280 281 contest = Contest.find(params[:contest][:id])
281 282 if !contest
282 283 flash[:notice] = 'You did not choose the contest.'
283 284 redirect_to :action => 'contest_management' and return
284 285 end
285 286
286 287 operation = params[:operation]
287 288
288 289 if not ['add','remove','assign'].include? operation
289 290 flash[:notice] = 'You did not choose the operation to perform.'
290 291 redirect_to :action => 'contest_management' and return
291 292 end
292 293
293 294 lines = params[:login_list]
294 295 if !lines or lines.blank?
295 296 flash[:notice] = 'You entered an empty list.'
296 297 redirect_to :action => 'contest_management' and return
297 298 end
298 299
299 300 note = []
300 301 users = []
301 302 lines.split("\n").each do |line|
302 303 user = User.find_by_login(line.chomp)
303 304 if user
304 305 if operation=='add'
305 306 if ! user.contests.include? contest
306 307 user.contests << contest
307 308 end
308 309 elsif operation=='remove'
309 310 user.contests.delete(contest)
310 311 else
311 312 user.contests = [contest]
312 313 end
313 314
314 315 if params[:reset_timer]
315 316 user.contest_stat.forced_logout = true
316 317 user.contest_stat.reset_timer_and_save
317 318 end
318 319
319 320 if params[:notification_emails]
320 321 send_contest_update_notification_email(user, contest)
321 322 end
322 323
323 324 note << user.login
324 325 users << user
325 326 end
326 327 end
327 328
328 329 if params[:reset_timer]
329 330 logout_users(users)
330 331 end
331 332
332 333 flash[:notice] = 'User(s) ' + note.join(', ') +
333 334 ' were successfully modified. '
334 335 redirect_to :action => 'contest_management'
335 336 end
336 337
337 338 # admin management
338 339
339 340 def admin
340 341 @admins = User.all.find_all {|user| user.admin? }
341 342 end
342 343
343 344 def grant_admin
344 345 login = params[:login]
345 346 user = User.find_by_login(login)
346 347 if user!=nil
347 348 admin_role = Role.find_by_name('admin')
348 349 user.roles << admin_role
349 350 else
350 351 flash[:notice] = 'Unknown user'
351 352 end
352 353 flash[:notice] = 'User added as admins'
353 354 redirect_to :action => 'admin'
354 355 end
355 356
356 357 def revoke_admin
357 358 user = User.find(params[:id])
358 359 if user==nil
359 360 flash[:notice] = 'Unknown user'
360 361 redirect_to :action => 'admin' and return
361 362 elsif user.login == 'root'
362 363 flash[:notice] = 'You cannot revoke admisnistrator permission from root.'
363 364 redirect_to :action => 'admin' and return
364 365 end
365 366
366 367 admin_role = Role.find_by_name('admin')
367 368 user.roles.delete(admin_role)
368 369 flash[:notice] = 'User permission revoked'
369 370 redirect_to :action => 'admin'
370 371 end
371 372
372 373 # mass mailing
373 374
374 375 def mass_mailing
375 376 end
376 377
377 378 def bulk_mail
378 379 lines = params[:login_list]
379 380 if !lines or lines.blank?
380 381 flash[:notice] = 'You entered an empty list.'
381 382 redirect_to :action => 'mass_mailing' and return
382 383 end
383 384
384 385 mail_subject = params[:subject]
385 386 if !mail_subject or mail_subject.blank?
386 387 flash[:notice] = 'You entered an empty mail subject.'
387 388 redirect_to :action => 'mass_mailing' and return
388 389 end
389 390
390 391 mail_body = params[:email_body]
391 392 if !mail_body or mail_body.blank?
392 393 flash[:notice] = 'You entered an empty mail body.'
393 394 redirect_to :action => 'mass_mailing' and return
394 395 end
395 396
396 397 note = []
397 398 users = []
398 399 lines.split("\n").each do |line|
399 400 user = User.find_by_login(line.chomp)
400 401 if user
401 402 send_mail(user.email, mail_subject, mail_body)
402 403 note << user.login
403 404 end
404 405 end
405 406
406 407 flash[:notice] = 'User(s) ' + note.join(', ') +
407 408 ' were successfully modified. '
408 409 redirect_to :action => 'mass_mailing'
409 410 end
410 411
411 412 #bulk manage
412 413 def bulk_manage
413 414
414 415 begin
415 416 @users = User.where('(login REGEXP ?) OR (remark REGEXP ?)',params[:regex],params[:regex]) if params[:regex]
416 417 @users.count if @users #i don't know why I have to call count, but if I won't exception is not raised
417 418 rescue Exception
418 419 flash[:error] = 'Regular Expression is malformed'
419 420 @users = nil
420 421 end
421 422
422 423 if params[:commit]
423 424 @action = {}
424 425 @action[:set_enable] = params[:enabled]
425 426 @action[:enabled] = params[:enable] == "1"
426 427 @action[:gen_password] = params[:gen_password]
427 428 @action[:add_group] = params[:add_group]
428 429 @action[:group_name] = params[:group_name]
429 430 end
430 431
431 432 if params[:commit] == "Perform"
432 433 if @action[:set_enable]
433 434 @users.update_all(enabled: @action[:enabled])
434 435 end
435 436 if @action[:gen_password]
436 437 @users.each do |u|
437 438 password = random_password
438 439 u.password = password
439 440 u.password_confirmation = password
440 441 u.save
441 442 end
442 443 end
443 444 if @action[:add_group] and @action[:group_name]
444 445 @group = Group.find(@action[:group_name])
445 446 ok = []
446 447 failed = []
447 448 @users.each do |user|
448 449 begin
449 450 @group.users << user
450 451 ok << user.login
451 452 rescue => e
452 453 failed << user.login
453 454 end
454 455 end
455 456 flash[:success] = "The following users are added to the 'group #{@group.name}': " + ok.join(', ') if ok.count > 0
456 457 flash[:alert] = "The following users are already in the 'group #{@group.name}': " + failed.join(', ') if failed.count > 0
457 458 end
458 459 end
459 460 end
460 461
461 462 protected
462 463
463 464 def random_password(length=5)
464 465 chars = 'abcdefghijkmnopqrstuvwxyz23456789'
465 466 newpass = ""
466 467 length.times { newpass << chars[rand(chars.size-1)] }
467 468 return newpass
468 469 end
469 470
470 471 def import_from_file(f)
471 472 data_hash = YAML.load(f)
472 473 @import_log = ""
473 474
474 475 country_data = data_hash[:countries]
475 476 site_data = data_hash[:sites]
476 477 user_data = data_hash[:users]
477 478
478 479 # import country
479 480 countries = {}
480 481 country_data.each_pair do |id,country|
481 482 c = Country.find_by_name(country[:name])
482 483 if c!=nil
483 484 countries[id] = c
484 485 @import_log << "Found #{country[:name]}\n"
485 486 else
486 487 countries[id] = Country.new(:name => country[:name])
487 488 countries[id].save
488 489 @import_log << "Created #{country[:name]}\n"
489 490 end
490 491 end
491 492
492 493 # import sites
493 494 sites = {}
494 495 site_data.each_pair do |id,site|
495 496 s = Site.find_by_name(site[:name])
496 497 if s!=nil
497 498 @import_log << "Found #{site[:name]}\n"
498 499 else
499 500 s = Site.new(:name => site[:name])
500 501 @import_log << "Created #{site[:name]}\n"
501 502 end
502 503 s.password = site[:password]
503 504 s.country = countries[site[:country_id]]
504 505 s.save
505 506 sites[id] = s
506 507 end
507 508
508 509 # import users
509 510 user_data.each_pair do |id,user|
510 511 u = User.find_by_login(user[:login])
511 512 if u!=nil
512 513 @import_log << "Found #{user[:login]}\n"
513 514 else
514 515 u = User.new(:login => user[:login])
515 516 @import_log << "Created #{user[:login]}\n"
516 517 end
517 518 u.full_name = user[:name]
518 519 u.password = user[:password]
519 520 u.country = countries[user[:country_id]]
520 521 u.site = sites[user[:site_id]]
521 522 u.activated = true
522 523 u.email = "empty-#{u.login}@none.com"
523 524 if not u.save
524 525 @import_log << "Errors\n"
525 526 u.errors.each { |attr,msg| @import_log << "#{attr} - #{msg}\n" }
526 527 end
527 528 end
528 529
529 530 end
530 531
531 532 def logout_users(users)
532 533 users.each do |user|
533 534 contest_stat = user.contest_stat(true)
534 535 if contest_stat and !contest_stat.forced_logout
535 536 contest_stat.forced_logout = true
536 537 contest_stat.save
537 538 end
538 539 end
539 540 end
540 541
541 542 def send_contest_update_notification_email(user, contest)
542 543 contest_title_name = GraderConfiguration['contest.name']
543 544 contest_name = contest.name
544 545 mail_subject = t('contest.notification.email_subject', {
545 546 :contest_title_name => contest_title_name,
546 547 :contest_name => contest_name })
547 548 mail_body = t('contest.notification.email_body', {
548 549 :full_name => user.full_name,
549 550 :contest_title_name => contest_title_name,
550 551 :contest_name => contest.name,
551 552 })
552 553
553 554 logger.info mail_body
554 555 send_mail(user.email, mail_subject, mail_body)
555 556 end
556 557
557 558 def find_contest_and_user_from_contest_id(id)
558 559 if id!='none'
559 560 @contest = Contest.find(id)
560 561 else
561 562 @contest = nil
562 563 end
563 564 if @contest
564 565 @users = @contest.users
565 566 else
566 567 @users = User.find_users_with_no_contest
567 568 end
568 569 return [@contest, @users]
569 570 end
570 571
571 572 def gen_csv_from_scorearray(scorearray,problem)
572 573 CSV.generate do |csv|
573 574 #add header
574 575 header = ['User','Name', 'Activated?', 'Logged in', 'Contest']
575 576 problem.each { |p| header << p.name }
576 577 header += ['Total','Passed']
577 578 csv << header
578 579 #add data
579 580 scorearray.each do |sc|
580 581 total = num_passed = 0
581 582 row = Array.new
582 583 sc.each_index do |i|
583 584 if i == 0
584 585 row << sc[i].login
585 586 row << sc[i].full_name
586 587 row << sc[i].activated
587 588 row << (sc[i].try(:contest_stat).try(:started_at).nil? ? 'no' : 'yes')
588 589 row << sc[i].contests.collect {|c| c.name}.join(', ')
589 590 else
590 591 row << sc[i][0]
591 592 total += sc[i][0]
592 593 num_passed += 1 if sc[i][1]
593 594 end
594 595 end
595 596 row << total
596 597 row << num_passed
597 598 csv << row
598 599 end
599 600 end
600 601 end
601 602
602 603 private
603 604 def user_params
604 605 params.require(:user).permit(:login,:password,:password_confirmation,:email, :alias, :full_name,:remark)
605 606 end
606 607 end
@@ -1,94 +1,107
1 1 - content_for :head do
2 2 = stylesheet_link_tag 'problems'
3 3 = javascript_include_tag 'local_jquery'
4 4
5 5 :javascript
6 6 $(document).ready( function() {
7 7 function shiftclick(start,stop,value) {
8 8 $('tr input').each( function(id,input) {
9 9 var $input=$(input);
10 10 var iid=parseInt($input.attr('id').split('-')[2]);
11 11 if(iid>=start&&iid<=stop){
12 12 $input.prop('checked',value)
13 13 }
14 14 });
15 15 }
16 16
17 17 $('tr input').click( function(e) {
18 18 if (e.shiftKey) {
19 19 stop = parseInt($(this).attr('id').split('-')[2]);
20 20 var orig_stop = stop
21 21 if (typeof start !== 'undefined') {
22 22 if (start > stop) {
23 23 var tmp = start;
24 24 start = stop;
25 25 stop = tmp;
26 26 }
27 27 shiftclick(start,stop,$(this).is(':checked') )
28 28 }
29 29 start = orig_stop
30 30 } else {
31 31 start = parseInt($(this).attr('id').split('-')[2]);
32 32 }
33 33 });
34 34 });
35 35
36 36
37 37 %h1 Manage problems
38 38
39 39 %p= link_to '[Back to problem list]', problems_path
40 40
41 41 = form_tag :action=>'do_manage' do
42 42 .panel.panel-primary
43 43 .panel-heading
44 44 Action
45 45 .panel-body
46 46 .submit-box
47 47 What do you want to do to the selected problem?
48 48 %br/
49 49 (You can shift-click to select a range of problems)
50 - %ul
50 + %ul.form-inline
51 51 %li
52 52 Change date added to
53 - = select_date Date.current, :prefix => 'date_added'
53 + .input-group.date
54 + = text_field_tag :date_added, class: 'form-control'
55 + %span.input-group-addon
56 + %span.glyphicon.glyphicon-calendar
57 + -# = select_date Date.current, :prefix => 'date_added'
54 58 &nbsp;&nbsp;&nbsp;
55 - = submit_tag 'Change', :name => 'change_date_added', class: 'btn btn-default'
59 + = submit_tag 'Change', :name => 'change_date_added', class: 'btn btn-primary btn-sm'
56 60 %li
57 61 Set available to
58 - = submit_tag 'True', :name => 'enable_problem', class: 'btn btn-default'
59 - = submit_tag 'False', :name => 'disable_problem', class: 'btn btn-default'
62 + = submit_tag 'True', :name => 'enable_problem', class: 'btn btn-primary btn-sm'
63 + = submit_tag 'False', :name => 'disable_problem', class: 'btn btn-primary btn-sm'
60 64
61 65 - if GraderConfiguration.multicontests?
62 66 %li
63 67 Add to
64 68 = select("contest","id",Contest.all.collect {|c| [c.title, c.id]})
65 - = submit_tag 'Add', :name => 'add_to_contest', class: 'btn btn-default'
69 + = submit_tag 'Add', :name => 'add_to_contest', class: 'btn btn-primary btn-sm'
66 70 %li
67 71 Add problems to group
68 72 = select_tag "group_id", options_from_collection_for_select( Group.all, 'id','name',params[:group_name]), id: 'group_name',class: 'select2'
69 73 = submit_tag 'Add', name: 'add_group', class: 'btn btn-default'
70 74
71 75
72 76 %table.table.table-hover
73 77 %tr{style: "text-align: left;"}
74 78 %th= check_box_tag 'select_all'
75 79 %th Name
76 80 %th Full name
77 81 %th Available
78 82 %th Date added
79 83 - if GraderConfiguration.multicontests?
80 84 %th Contests
81 85
82 86 - num = 0
83 87 - for problem in @problems
84 88 - num += 1
85 89 %tr{:id => "row-prob-#{problem.id}", :name=> "prob-#{problem.id}"}
86 90 %td= check_box_tag "prob-#{problem.id}-#{num}"
87 91 %td= problem.name
88 92 %td= problem.full_name
89 93 %td= problem.available
90 94 %td= problem.date_added
91 95 - if GraderConfiguration.multicontests?
92 96 %td
93 97 - problem.contests.each do |contest|
94 98 = "(#{contest.name} [#{link_to 'x', :action => 'remove_contest', :id => problem.id, :contest_id => contest.id }])"
99 +
100 + :javascript
101 + $('.input-group.date').datetimepicker({
102 + format: 'DD/MMM/YYYY',
103 + showTodayButton: true,
104 + widgetPositioning: {horizontal: 'auto', vertical: 'bottom'},
105 +
106 + });
107 +
@@ -1,53 +1,57
1 1 :css
2 2 .fix-width {
3 3 font-family: "Consolas, Monaco, Droid Sans Mono,Mono, Monospace,Courier"
4 4 }
5 5
6 6 %h1 Problem stat: #{@problem.name}
7 7 %h2 Overview
8 8
9 9
10 10 %table.info
11 11 %thead
12 12 %tr.info-head
13 13 %th Stat
14 14 %th Value
15 15 %tbody
16 16 %tr{class: cycle('info-even','info-odd')}
17 17 %td Submissions
18 18 %td= @submissions.count
19 19 %tr{class: cycle('info-even','info-odd')}
20 20 %td Solved/Attempted User
21 21 %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
22 22
23 23 %h2 Submissions Count
24 24 = render partial: 'application/bar_graph', locals: { histogram: @histogram }
25 25
26 26 %h2 Submissions
27 27 - if @submissions and @submissions.count > 0
28 - %table.info#main_table
28 + %table#main_table.table.table-condensed.table-striped
29 29 %thead
30 - %tr.info-head
30 + %tr
31 31 %th ID
32 32 %th Login
33 33 %th Name
34 34 %th Submitted_at
35 35 %th Points
36 36 %th comment
37 37 %th IP
38 38 %tbody
39 39 - row_odd,curr = true,''
40 40 - @submissions.each do |sub|
41 41 - next unless sub.user
42 42 - row_odd,curr = !row_odd, sub.user if curr != sub.user
43 - %tr{class: row_odd ? "info-odd" : "info-even"}
43 + %tr
44 44 %td= link_to sub.id, submission_path(sub)
45 45 %td= link_to sub.user.login, stat_user_path(sub.user)
46 46 %td= sub.user.full_name
47 - %td= time_ago_in_words(sub.submitted_at) + " ago"
47 + %td{data: {order: sub.submitted_at}}= time_ago_in_words(sub.submitted_at) + " ago"
48 48 %td= sub.points
49 49 %td.fix-width= sub.grader_comment
50 50 %td= sub.ip_address
51 51 - else
52 52 No submission
53 53
54 + :javascript
55 + $("#main_table").DataTable({
56 + paging: false
57 + });
@@ -1,24 +1,35
1 1 %h1 Editing site
2 2 = error_messages_for :site
3 3 = form_for(@site) do |f|
4 - %p
5 - %b Name
6 - %br/
7 - = f.text_field :name
8 - %p
9 - %b Password
10 - %br/
11 - = f.text_field :password
12 - %p
13 - %b Started
14 - %br/
15 - = f.check_box :started
16 - %p
17 - %b Start time
18 - %br/
19 - = f.datetime_select :start_time, :include_blank => true
20 - %p
21 - = f.submit "Update"
4 + .row
5 + .col-md-4
6 + .form-group.field
7 + = f.label :name, "Name"
8 + = f.text_field :name, class: 'form-control'
9 + .form-group.field
10 + = f.label :password, "Password"
11 + = f.text_field :password, class: 'form-control'
12 + .form-group.field
13 + = f.label :started, "Started"
14 + = f.check_box :started, class: 'form-control'
15 + .form-group.field
16 + = f.label :start_time, "Start time"
17 + -# = f.datetime_select :start_time, :include_blank => true
18 + .input-group.date
19 + = f.text_field :start_time, class:'form-control' , value: (@site.start_time ? @site.start_time.strftime('%d/%b/%Y %H:%M') : '')
20 + %span.input-group-addon
21 + %span.glyphicon.glyphicon-calendar
22 + .actions
23 + = f.submit "Update", class: 'btn btn-primary'
24 + .col-md-8
25 +
22 26 = link_to 'Show', @site
23 27 |
24 28 = link_to 'Back', sites_path
29 +
30 +
31 + :javascript
32 + $('.input-group.date').datetimepicker({
33 + format: 'DD/MMM/YYYY HH:mm',
34 + });
35 +
@@ -1,101 +1,106
1 - %h1 Listing users
1 + %h1 Users
2 2
3 3 .panel.panel-primary
4 4 .panel-title.panel-heading
5 5 Quick Add
6 6 .panel-body
7 7 = form_tag( {method: 'post'}, {class: 'form-inline'}) do
8 8 .form-group
9 9 = label_tag 'user_login', 'Login'
10 10 = text_field 'user', 'login', :size => 10,class: 'form-control'
11 11 .form-group
12 12 = label_tag 'user_full_name', 'Full Name'
13 13 = text_field 'user', 'full_name', :size => 10,class: 'form-control'
14 14 .form-group
15 15 = label_tag 'user_password', 'Password'
16 16 = text_field 'user', 'password', :size => 10,class: 'form-control'
17 17 .form-group
18 18 = label_tag 'user_password_confirmation', 'Confirm'
19 19 = text_field 'user', 'password_confirmation', :size => 10,class: 'form-control'
20 20 .form-group
21 21 = label_tag 'user_email', 'email'
22 22 = text_field 'user', 'email', :size => 10,class: 'form-control'
23 23 =submit_tag "Create", class: 'btn btn-primary'
24 24
25 25 .panel.panel-primary
26 26 .panel-title.panel-heading
27 27 Import from site management
28 28 .panel-body
29 29 = form_tag({:action => 'import'}, :multipart => true,class: 'form form-inline') do
30 30 .form-group
31 31 = label_tag :file, 'File:'
32 32 .input-group
33 33 %span.input-group-btn
34 34 %span.btn.btn-default.btn-file
35 35 Browse
36 36 = file_field_tag 'file'
37 37 = text_field_tag '' , nil, {readonly: true, class: 'form-control'}
38 38 = submit_tag 'Submit', class: 'btn btn-default'
39 39
40 40
41 41 %p
42 42 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
43 43 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
44 + = link_to 'Bulk Manage', bulk_manage_user_admin_path , { class: 'btn btn-default btn-info'}
44 45 = link_to 'View administrators',{ :action => 'admin'}, { class: 'btn btn-default '}
45 - = link_to 'Bulk Manage', bulk_manage_user_admin_path , { class: 'btn btn-default '}
46 46 = link_to 'Random passwords',{ :action => 'random_all_passwords'}, { class: 'btn btn-default '}
47 47 = link_to 'View active users',{ :action => 'active'}, { class: 'btn btn-default '}
48 48 = link_to 'Mass mailing',{ :action => 'mass_mailing'}, { class: 'btn btn-default '}
49 49
50 50 - if GraderConfiguration.multicontests?
51 51 %br/
52 52 %b Multi-contest:
53 53 = link_to '[Manage bulk users in contests]', :action => 'contest_management'
54 54 View users in:
55 55 - @contests.each do |contest|
56 56 = link_to "[#{contest.name}]", :action => 'contests', :id => contest.id
57 57 = link_to "[no contest]", :action => 'contests', :id => 'none'
58 58
59 - Total #{@user_count} users |
60 - - if !@paginated
61 - Display all users.
62 - \#{link_to '[show in pages]', :action => 'index', :page => '1'}
63 - - else
64 - Display in pages.
65 - \#{link_to '[display all]', :action => 'index', :page => 'all'} |
66 - \#{will_paginate @users, :container => false}
59 + -# Total #{@user_count} users |
60 + -# - if !@paginated
61 + -# Display all users.
62 + -# \#{link_to '[show in pages]', :action => 'index', :page => '1'}
63 + -# - else
64 + -# Display in pages.
65 + -# \#{link_to '[display all]', :action => 'index', :page => 'all'} |
66 + -# \#{will_paginate @users, :container => false}
67 67
68 68
69 - %table.table.table-hover.table-condense
69 + %table.table.table-hover.table-condense.datatable
70 70 %thead
71 71 %th Login
72 72 %th Full name
73 73 %th email
74 74 %th Remark
75 75 %th
76 76 Activated
77 77 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'User has already confirmed the email?' } [?]
78 78 %th
79 79 Enabled
80 80 %sup{class: 'text-primary',data: {toggle: 'tooltip', placement: 'top'}, title: 'Allow the user to login?' } [?]
81 81 %th Last IP
82 82 %th
83 83 %th
84 84 %th
85 85 %th
86 86 - for user in @users
87 87 %tr
88 88 %td= link_to user.login, stat_user_path(user)
89 89 %td= user.full_name
90 90 %td= user.email
91 91 %td= user.remark
92 92 %td= toggle_button(user.activated?, toggle_activate_user_path(user),"toggle_activate_user_#{user.id}")
93 93 %td= toggle_button(user.enabled?, toggle_enable_user_path(user),"toggle_enable_user_#{user.id}")
94 94 %td= user.last_ip
95 95 %td= link_to 'Clear IP', {:action => 'clear_last_ip', :id => user, :page=>params[:page]}, :confirm => 'This will reset last logging in ip of the user, are you sure?', class: 'btn btn-default btn-xs btn-block'
96 96 %td= link_to 'Show', {:action => 'show', :id => user}, class: 'btn btn-default btn-xs btn-block'
97 97 %td= link_to 'Edit', {:action => 'edit', :id => user}, class: 'btn btn-default btn-xs btn-block'
98 98 %td= link_to 'Destroy', user_admin_destroy_path(user), data: {confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-danger btn-xs btn-block'
99 99 %br/
100 100 = link_to '+ New user', { :action => 'new' }, { class: 'btn btn-success '}
101 101 = link_to '+ New list of users', { :action => 'new_list' }, { class: 'btn btn-success '}
102 +
103 + :javascript
104 + $('.datatable').DataTable({
105 + 'pageLength': 50
106 + });
@@ -1,66 +1,70
1 1 - content_for :header do
2 2 = javascript_include_tag 'local_jquery'
3 3
4 4 :javascript
5 5 $(function () {
6 6 $('#submission_table').tablesorter({widgets: ['zebra']});
7 7 });
8 8
9 9 :css
10 10 .fix-width {
11 11 font-family: Droid Sans Mono,Consolas, monospace, mono, Courier New, Courier;
12 12 }
13 13
14 14 %h1= @user.full_name
15 15
16 16 <b>Login:</b> #{@user.login} <br/>
17 17 <b>Full name:</b> #{@user.full_name} <br />
18 18
19 19
20 20 %h2 Problem Stat
21 21 %table.info
22 22 %thead
23 23 %tr.info-head
24 24 %th Stat
25 25 %th Value
26 26 %tbody
27 27 %tr{class: cycle('info-even','info-odd')}
28 28 %td.info_param Submissions
29 29 %td= @summary[:count]
30 30 %tr{class: cycle('info-even','info-odd')}
31 31 %td.info_param Solved/Attempted Problem
32 32 %td #{@summary[:solve]}/#{@summary[:attempt]} (#{(@summary[:solve]*100.0/@summary[:attempt]).round(1)}%)
33 33
34 34 %h2 Submission History
35 35
36 36 =render partial: 'application/bar_graph', locals: {histogram: @histogram, param: {bar_width: 7}}
37 37
38 38
39 - %table.tablesorter-cafe#submission_table
39 + %table#submission_table.table.table-striped
40 40 %thead
41 41 %tr
42 42 %th ID
43 43 %th Problem code
44 44 %th Problem full name
45 45 %th Language
46 46 %th Submitted at
47 47 %th Result
48 48 %th Score
49 49 - if session[:admin]
50 50 %th IP
51 51 %tbody
52 52 - @submission.each do |s|
53 53 - next unless s.problem
54 54 %tr
55 55 %td= link_to s.id, submission_path(s)
56 56 %td= link_to s.problem.name, stat_problem_path(s.problem)
57 57 %td= s.problem.full_name
58 58 %td= s.language.pretty_name
59 59 %td #{s.submitted_at.strftime('%Y-%m-%d %H:%M')} (#{time_ago_in_words(s.submitted_at)} ago)
60 60 %td.fix-width= s.grader_comment
61 61 %td= ( s.try(:points) ? (s.points*100/s.problem.full_score) : '' )
62 62 - if session[:admin]
63 63 %td= s.ip_address
64 64
65 65
66 66
67 + :javascript
68 + $("#submission_table").DataTable({
69 + paging: false
70 + });
You need to be logged in to leave comments. Login now