Description:
[web] added mode + access control, when sites started/finished git-svn-id: http://theory.cpe.ku.ac.th/grader/web/trunk@250 6386c4cd-e34a-4fa8-8920-d93eb39b512e
Commit status:
[Not Reviewed]
References:
Comments:
0 Commit comments 0 Inline Comments
Unresolved TODOs:
There are no unresolved TODOs
Add another comment

r122:dfa0387efb1e - - 9 files changed: 188 inserted, 84 deleted

@@ -0,0 +1,17
1 + class AddModeToConfigurations < ActiveRecord::Migration
2 + def self.up
3 +
4 + # Configuration['system.mode']:
5 + # * 'standard' mode
6 + # * 'contest' mode (check site start time/stop time)
7 + # * 'analysis' mode (show results, no new submissions)
8 +
9 + Configuration.create(:key => 'system.mode',
10 + :value_type => 'string',
11 + :value => 'standard')
12 + end
13 +
14 + def self.down
15 + Configuration.find_by_key('system.mode').destroy
16 + end
17 + end
@@ -1,15 +1,18
1 class MainController < ApplicationController
1 class MainController < ApplicationController
2
2
3 + SYSTEM_MODE_CONF_KEY = 'system.mode'
4 +
3 before_filter :authenticate, :except => [:index, :login]
5 before_filter :authenticate, :except => [:index, :login]
6 + before_filter :check_viewability
4
7
5 #
8 #
6 # COMMENT OUT: filter in each action instead
9 # COMMENT OUT: filter in each action instead
7 #
10 #
8 # before_filter :verify_time_limit, :only => [:submit]
11 # before_filter :verify_time_limit, :only => [:submit]
9
12
10 verify :method => :post, :only => [:submit],
13 verify :method => :post, :only => [:submit],
11 :redirect_to => { :action => :index }
14 :redirect_to => { :action => :index }
12
15
13
16
14 def index
17 def index
15 redirect_to :action => 'login'
18 redirect_to :action => 'login'
@@ -34,25 +37,26
34 def submit
37 def submit
35 user = User.find(session[:user_id])
38 user = User.find(session[:user_id])
36
39
37 @submission = Submission.new(params[:submission])
40 @submission = Submission.new(params[:submission])
38 @submission.user = user
41 @submission.user = user
39 @submission.language_id = 0
42 @submission.language_id = 0
40 if params['file']!=''
43 if params['file']!=''
41 @submission.source = params['file'].read
44 @submission.source = params['file'].read
42 @submission.source_filename = params['file'].original_filename
45 @submission.source_filename = params['file'].original_filename
43 end
46 end
44 @submission.submitted_at = Time.new.gmtime
47 @submission.submitted_at = Time.new.gmtime
45
48
46 - if user.site!=nil and user.site.finished?
49 + if Configuration[SYSTEM_MODE_CONF_KEY]=='contest' and
50 + user.site!=nil and user.site.finished?
47 @submission.errors.add_to_base "The contest is over."
51 @submission.errors.add_to_base "The contest is over."
48 prepare_list_information
52 prepare_list_information
49 render :action => 'list' and return
53 render :action => 'list' and return
50 end
54 end
51
55
52 if @submission.valid?
56 if @submission.valid?
53 if @submission.save == false
57 if @submission.save == false
54 flash[:notice] = 'Error saving your submission'
58 flash[:notice] = 'Error saving your submission'
55 elsif Task.create(:submission_id => @submission.id,
59 elsif Task.create(:submission_id => @submission.id,
56 :status => Task::STATUS_INQUEUE) == false
60 :status => Task::STATUS_INQUEUE) == false
57 flash[:notice] = 'Error adding your submission to task queue'
61 flash[:notice] = 'Error adding your submission to task queue'
58 end
62 end
@@ -116,14 +120,22
116 if sub!=nil
120 if sub!=nil
117 @prob_submissions << { :count => sub.number, :submission => sub }
121 @prob_submissions << { :count => sub.number, :submission => sub }
118 else
122 else
119 @prob_submissions << { :count => 0, :submission => nil }
123 @prob_submissions << { :count => 0, :submission => nil }
120 end
124 end
121 end
125 end
122
126
123 @announcements = Announcement.find(:all,
127 @announcements = Announcement.find(:all,
124 :conditions => "published = 1",
128 :conditions => "published = 1",
125 :order => "created_at DESC")
129 :order => "created_at DESC")
126 end
130 end
127
131
132 + def check_viewability
133 + user = User.find(session[:user_id])
134 + if (!Configuration.show_tasks_to?(user)) and
135 + ((action_name=='submission') or (action_name=='submit'))
136 + redirect_to :action => 'list' and return
137 + end
138 + end
139 +
128 end
140 end
129
141
@@ -1,15 +1,24
1 class TasksController < ApplicationController
1 class TasksController < ApplicationController
2
2
3 - before_filter :authenticate
3 + before_filter :authenticate, :check_viewability
4 -
5
4
6 def index
5 def index
7 redirect_to :action => 'list'
6 redirect_to :action => 'list'
8 end
7 end
9
8
10 def list
9 def list
11 @problems = Problem.find_available_problems
10 @problems = Problem.find_available_problems
12 @user = User.find(session[:user_id])
11 @user = User.find(session[:user_id])
13 end
12 end
14
13
14 + protected
15 +
16 + def check_viewability
17 + user = User.find(session[:user_id])
18 + if user==nil or !Configuration.show_tasks_to?(user)
19 + redirect_to :controller => 'main', :action => 'list'
20 + return false
21 + end
22 + end
23 +
15 end
24 end
@@ -1,39 +1,42
1 class TestController < ApplicationController
1 class TestController < ApplicationController
2
2
3 - before_filter :authenticate
3 + SYSTEM_MODE_CONF_KEY = 'system.mode'
4 +
5 + before_filter :authenticate, :check_viewability
4
6
5 #
7 #
6 # COMMENT OUT: filter in each action instead
8 # COMMENT OUT: filter in each action instead
7 #
9 #
8 # before_filter :verify_time_limit, :only => [:submit]
10 # before_filter :verify_time_limit, :only => [:submit]
9
11
10 verify :method => :post, :only => [:submit],
12 verify :method => :post, :only => [:submit],
11 :redirect_to => { :action => :index }
13 :redirect_to => { :action => :index }
12
14
13 def index
15 def index
14 prepare_index_information
16 prepare_index_information
15 end
17 end
16
18
17 def submit
19 def submit
18 @user = User.find(session[:user_id])
20 @user = User.find(session[:user_id])
19
21
20 @submitted_test_request = TestRequest.new_from_form_params(@user,params[:test_request])
22 @submitted_test_request = TestRequest.new_from_form_params(@user,params[:test_request])
21
23
22 if @submitted_test_request.errors.length != 0
24 if @submitted_test_request.errors.length != 0
23 prepare_index_information
25 prepare_index_information
24 render :action => 'index' and return
26 render :action => 'index' and return
25 end
27 end
26
28
27 - if @user.site!=nil and @user.site.finished?
29 + if Configuration[SYSTEM_MODE_CONF_KEY]=='contest' and
30 + @user.site!=nil and @user.site.finished?
28 @submitted_test_request.errors.add_to_base('Contest is over.')
31 @submitted_test_request.errors.add_to_base('Contest is over.')
29 prepare_index_information
32 prepare_index_information
30 render :action => 'index' and return
33 render :action => 'index' and return
31 end
34 end
32
35
33 if @submitted_test_request.save
36 if @submitted_test_request.save
34 redirect_to :action => 'index'
37 redirect_to :action => 'index'
35 else
38 else
36 prepare_index_information
39 prepare_index_information
37 render :action => 'index'
40 render :action => 'index'
38 end
41 end
39 end
42 end
@@ -83,13 +86,23
83 @user = User.find(session[:user_id])
86 @user = User.find(session[:user_id])
84 @submissions = Submission.find_last_for_all_available_problems(@user.id)
87 @submissions = Submission.find_last_for_all_available_problems(@user.id)
85 all_problems = @submissions.collect { |submission| submission.problem }
88 all_problems = @submissions.collect { |submission| submission.problem }
86 @problems = []
89 @problems = []
87 all_problems.each do |problem|
90 all_problems.each do |problem|
88 if problem.test_allowed
91 if problem.test_allowed
89 @problems << problem
92 @problems << problem
90 end
93 end
91 end
94 end
92 @test_requests = @user.test_requests
95 @test_requests = @user.test_requests
93 end
96 end
94
97
98 + def check_viewability
99 + user = User.find(session[:user_id])
100 + if !Configuration.show_tasks_to?(user)
101 + redirect_to :controller => 'main', :action => 'list'
102 + end
103 + if (!Configuration.show_submitbox_to?(user)) and (action_name=='submit')
104 + redirect_to :controller => 'test', :action => 'index'
105 + end
106 + end
107 +
95 end
108 end
@@ -1,84 +1,106
1 # Methods added to this helper will be available to all templates in the application.
1 # Methods added to this helper will be available to all templates in the application.
2 module ApplicationHelper
2 module ApplicationHelper
3
3
4 + SYSTEM_MODE_CONF_KEY = 'system.mode'
5 +
4 def user_header
6 def user_header
5 menu_items = ''
7 menu_items = ''
6 user = User.find(session[:user_id])
8 user = User.find(session[:user_id])
7
9
8 if (user!=nil) and (session[:admin])
10 if (user!=nil) and (session[:admin])
9 # admin menu
11 # admin menu
10 menu_items << "<b>Administrative task:</b> "
12 menu_items << "<b>Administrative task:</b> "
11 append_to menu_items, '[Announcements]', 'announcements', 'index'
13 append_to menu_items, '[Announcements]', 'announcements', 'index'
12 append_to menu_items, '[Msg console]', 'messages', 'console'
14 append_to menu_items, '[Msg console]', 'messages', 'console'
13 append_to menu_items, '[Problem admin]', 'problems', 'index'
15 append_to menu_items, '[Problem admin]', 'problems', 'index'
14 append_to menu_items, '[User admin]', 'user_admin', 'index'
16 append_to menu_items, '[User admin]', 'user_admin', 'index'
15 append_to menu_items, '[User stat]', 'user_admin', 'user_stat'
17 append_to menu_items, '[User stat]', 'user_admin', 'user_stat'
16 append_to menu_items, '[Graders]', 'graders', 'list'
18 append_to menu_items, '[Graders]', 'graders', 'list'
17 append_to menu_items, '[Site config]', 'configurations', 'index'
19 append_to menu_items, '[Site config]', 'configurations', 'index'
18 menu_items << "<br/>"
20 menu_items << "<br/>"
19 end
21 end
20
22
21 # main page
23 # main page
22 append_to menu_items, '[Main]', 'main', 'list'
24 append_to menu_items, '[Main]', 'main', 'list'
23 append_to menu_items, '[Messages]', 'messages', 'list'
25 append_to menu_items, '[Messages]', 'messages', 'list'
24 - append_to menu_items, '[Tasks]', 'tasks', 'list'
26 +
25 - append_to menu_items, '[Submissions]', 'main', 'submission'
27 + if (user!=nil) and (Configuration.show_tasks_to?(user))
26 - append_to menu_items, '[Test]', 'test', 'index'
28 + append_to menu_items, '[Tasks]', 'tasks', 'list'
29 + append_to menu_items, '[Submissions]', 'main', 'submission'
30 + append_to menu_items, '[Test]', 'test', 'index'
31 + end
27 append_to menu_items, '[Help]', 'main', 'help'
32 append_to menu_items, '[Help]', 'main', 'help'
28 #append_to menu_items, '[Settings]', 'users', 'index'
33 #append_to menu_items, '[Settings]', 'users', 'index'
29 append_to menu_items, '[Log out]', 'main', 'login'
34 append_to menu_items, '[Log out]', 'main', 'login'
30
35
31 menu_items
36 menu_items
32 end
37 end
33
38
34 def append_to(option,label, controller, action)
39 def append_to(option,label, controller, action)
35 option << ' ' if option!=''
40 option << ' ' if option!=''
36 option << link_to_unless_current(label,
41 option << link_to_unless_current(label,
37 :controller => controller,
42 :controller => controller,
38 :action => action)
43 :action => action)
39 end
44 end
40
45
41 def format_short_time(time)
46 def format_short_time(time)
42 now = Time.now.gmtime
47 now = Time.now.gmtime
43 st = ''
48 st = ''
44 if (time.yday != now.yday) or
49 if (time.yday != now.yday) or
45 (time.year != now.year)
50 (time.year != now.year)
46 st = time.strftime("%x ")
51 st = time.strftime("%x ")
47 end
52 end
48 st + time.strftime("%X")
53 st + time.strftime("%X")
49 end
54 end
50
55
56 + def read_textfile(fname,max_size=2048)
57 + begin
58 + File.open(fname).read(max_size)
59 + rescue
60 + nil
61 + end
62 + end
51
63
52 def user_title_bar(user)
64 def user_title_bar(user)
53 - if user.site!=nil and user.site.finished?
65 + header = ''
54 - contest_over_string = <<CONTEST_OVER
66 +
67 + #
68 + # if the contest is over
69 + if Configuration[SYSTEM_MODE_CONF_KEY]=='contest' and
70 + user.site!=nil and user.site.finished?
71 + header = <<CONTEST_OVER
55 <tr><td colspan="2" align="center">
72 <tr><td colspan="2" align="center">
56 <span class="contest-over-msg">THE CONTEST IS OVER</span>
73 <span class="contest-over-msg">THE CONTEST IS OVER</span>
57 </td></tr>
74 </td></tr>
58 CONTEST_OVER
75 CONTEST_OVER
59 end
76 end
77 +
78 + #
79 + # if the contest is in the anaysis mode
80 + if Configuration[SYSTEM_MODE_CONF_KEY]=='analysis'
81 + header = <<ANALYSISMODE
82 + <tr><td colspan="2" align="center">
83 + <span class="contest-over-msg">ANALYSIS MODE</span>
84 + </td></tr>
85 + ANALYSISMODE
86 + end
87 +
88 + #
89 + # build real title bar
60 <<TITLEBAR
90 <<TITLEBAR
61 <div class="title">
91 <div class="title">
62 <table>
92 <table>
63 - #{contest_over_string}
93 + #{header}
64 <tr>
94 <tr>
65 <td class="left-col">
95 <td class="left-col">
66 #{user.full_name}<br/>
96 #{user.full_name}<br/>
67 Current time is #{format_short_time(Time.new.gmtime)} UTC<br/>
97 Current time is #{format_short_time(Time.new.gmtime)} UTC<br/>
68 </td>
98 </td>
69 <td class="right-col">APIO'08</td>
99 <td class="right-col">APIO'08</td>
70 </tr>
100 </tr>
71 </table>
101 </table>
72 </div>
102 </div>
73 TITLEBAR
103 TITLEBAR
74 end
104 end
75
105
76 - def read_textfile(fname,max_size=2048)
77 - begin
78 - File.open(fname).read(max_size)
79 - rescue
80 - nil
81 - end
82 - end
83 -
84 end
106 end
@@ -1,35 +1,62
1 + #
2 + # This class also contains various login of the system.
3 + #
1 class Configuration < ActiveRecord::Base
4 class Configuration < ActiveRecord::Base
2
5
6 + SYSTEM_MODE_CONF_KEY = 'system.mode'
7 +
3 @@configurations = nil
8 @@configurations = nil
4
9
5 def self.get(key)
10 def self.get(key)
6 if @@configurations == nil
11 if @@configurations == nil
7 self.read_config
12 self.read_config
8 end
13 end
9 return @@configurations[key]
14 return @@configurations[key]
10 end
15 end
11
16
12 def self.[](key)
17 def self.[](key)
13 self.get(key)
18 self.get(key)
14 end
19 end
15
20
16 def self.reload
21 def self.reload
17 self.read_config
22 self.read_config
18 end
23 end
19
24
20 def self.clear
25 def self.clear
21 @@configurations = nil
26 @@configurations = nil
22 end
27 end
23
28
29 + #
30 + # View decision
31 + #
32 + def self.show_submitbox_to?(user)
33 + mode = get(SYSTEM_MODE_CONF_KEY)
34 + return false if mode=='analysis'
35 + if (mode=='contest')
36 + return false if (user.site!=nil) and
37 + ((user.site.started==false) or (user.site.finished?))
38 + end
39 + return true
40 + end
41 +
42 + def self.show_tasks_to?(user)
43 + mode = get(SYSTEM_MODE_CONF_KEY)
44 + if (mode=='contest')
45 + return false if (user.site!=nil) and (user.site.started==false)
46 + end
47 + return true
48 + end
49 +
50 +
24 protected
51 protected
25 def self.read_config
52 def self.read_config
26 @@configurations = {}
53 @@configurations = {}
27 Configuration.find(:all).each do |conf|
54 Configuration.find(:all).each do |conf|
28 key = conf.key
55 key = conf.key
29 val = conf.value
56 val = conf.value
30 case conf.value_type
57 case conf.value_type
31 when 'string'
58 when 'string'
32 @@configurations[key] = val
59 @@configurations[key] = val
33
60
34 when 'integer'
61 when 'integer'
35 @@configurations[key] = val.to_i
62 @@configurations[key] = val.to_i
@@ -1,30 +1,32
1 = user_title_bar(@user)
1 = user_title_bar(@user)
2
2
3 - if @announcements.length!=0
3 - if @announcements.length!=0
4 .announcementbox
4 .announcementbox
5 %span{:class => 'title'}
5 %span{:class => 'title'}
6 Announcements
6 Announcements
7 = render :partial => 'announcement', :collection => @announcements
7 = render :partial => 'announcement', :collection => @announcements
8
8
9 - .submitbox
9 + - if Configuration.show_submitbox_to?(@user)
10 - = error_messages_for 'submission'
10 + .submitbox
11 - = render :partial => 'submission_box'
11 + = error_messages_for 'submission'
12 + = render :partial => 'submission_box'
12
13
13
14
14 %hr/
15 %hr/
15
16
16 - %table.info
17 + - if Configuration.show_tasks_to?(@user)
17 - %tr.info-head
18 + %table.info
18 - %th
19 + %tr.info-head
19 - %th Tasks
20 + %th
20 - %th # of sub(s)
21 + %th Tasks
21 - %th Results
22 + %th # of sub(s)
22 - = render :partial => 'problem', :collection => @problems
23 + %th Results
24 + = render :partial => 'problem', :collection => @problems
23
25
24 %hr/
26 %hr/
25
27
26 %p
28 %p
27 %b Note:
29 %b Note:
28 We currently have problems synchronizing
30 We currently have problems synchronizing
29 the time stamps between grading machines.
31 the time stamps between grading machines.
30 You will see weird time stamps during the practice session.
32 You will see weird time stamps during the practice session.
@@ -21,79 +21,81
21 for(i=0; i<old_len; i++)
21 for(i=0; i<old_len; i++)
22 submissionSelect.remove(0);
22 submissionSelect.remove(0);
23 for(i=count; i>=1; i--) {
23 for(i=count; i>=1; i--) {
24 try {
24 try {
25 submissionSelect.add(new Option(""+i,""+i,false,false),null);
25 submissionSelect.add(new Option(""+i,""+i,false,false),null);
26 } catch(ex) {
26 } catch(ex) {
27 submissionSelect.add(new Option(""+i,""+i,false,false));
27 submissionSelect.add(new Option(""+i,""+i,false,false));
28 }
28 }
29 }
29 }
30 }
30 }
31 </script>
31 </script>
32
32
33 - <div class="submitbox">
33 + <% if Configuration.show_submitbox_to?(@user) %>
34 - <%= error_messages_for 'submitted_test_request' %>
34 + <div class="submitbox">
35 - <% form_for :test_request, nil,
35 + <%= error_messages_for 'submitted_test_request' %>
36 - :url => { :action => 'submit'},
36 + <% form_for :test_request, nil,
37 - :html => { :multipart => true } do |f| %>
37 + :url => { :action => 'submit'},
38 - <table>
38 + :html => { :multipart => true } do |f| %>
39 - <tr>
39 + <table>
40 - <td>Task:</td>
40 + <tr>
41 - <td>
41 + <td>Task:</td>
42 - <%= select(:test_request,
42 + <td>
43 - :problem_id,
43 + <%= select(:test_request,
44 - @problems.collect {|p| [p.name, p.id]}, {},
44 + :problem_id,
45 - { :onclick => "updateSubmissionList();" }) %>
45 + @problems.collect {|p| [p.name, p.id]}, {},
46 - </td>
46 + { :onclick => "updateSubmissionList();" }) %>
47 - </tr>
47 + </td>
48 - <tr>
48 + </tr>
49 - <td>Submission:</td>
49 + <tr>
50 - <td>
50 + <td>Submission:</td>
51 - <%= select(:test_request,
51 + <td>
52 - :submission_number,
52 + <%= select(:test_request,
53 - ((1..@submissions[0].number).collect {|n| [n,n]}).reverse) %>
53 + :submission_number,
54 - </td>
54 + ((1..@submissions[0].number).collect {|n| [n,n]}).reverse) %>
55 - </tr>
55 + </td>
56 - <tr>
56 + </tr>
57 - <td>Input data:</td>
57 + <tr>
58 - <td>
58 + <td>Input data:</td>
59 - <%= f.file_field :input_file %>
59 + <td>
60 - </td>
60 + <%= f.file_field :input_file %>
61 - <td>
61 + </td>
62 - (combined size should not exceed 2MB)
62 + <td>
63 - </td>
63 + (combined size should not exceed 2MB)
64 - </tr>
64 + </td>
65 - <tr>
65 + </tr>
66 - <td>
66 + <tr>
67 - Additional file<sup><span style="color:red">*</span></sup>:
67 + <td>
68 - </td>
68 + Additional file<sup><span style="color:red">*</span></sup>:
69 - <td>
69 + </td>
70 - <%= f.file_field :additional_file %>
70 + <td>
71 - </td>
71 + <%= f.file_field :additional_file %>
72 - <td>
72 + </td>
73 - <small>
73 + <td>
74 - * This option works <u>only</u> for task max.
74 + <small>
75 - You can use this to submit <tt>questions.txt</tt>.<br/>
75 + * This option works <u>only</u> for task max.
76 - The file shall be copied to the execution directory before your program runs.
76 + You can use this to submit <tt>questions.txt</tt>.<br/>
77 - </small>
77 + The file shall be copied to the execution directory before your program runs.
78 - </td>
78 + </small>
79 - </tr>
79 + </td>
80 - <tr>
80 + </tr>
81 - <td colspan="2">
81 + <tr>
82 - <%= submit_tag 'submit' %>
82 + <td colspan="2">
83 - </td>
83 + <%= submit_tag 'submit' %>
84 - </tr>
84 + </td>
85 - </table>
85 + </tr>
86 + </table>
87 + <% end %>
88 + </div>
86 <% end %>
89 <% end %>
87 - </div>
88
90
89 <h3>Previous requests</h3>
91 <h3>Previous requests</h3>
90
92
91 <table class="info">
93 <table class="info">
92 <tr class="info-head">
94 <tr class="info-head">
93 <th>at</th>
95 <th>at</th>
94 <th>problem</th>
96 <th>problem</th>
95 <th>sub #</th>
97 <th>sub #</th>
96 <th>status</th>
98 <th>status</th>
97 <th>output (first 2kb)</th>
99 <th>output (first 2kb)</th>
98 <th>compiler message</th>
100 <th>compiler message</th>
99 <th>detail</th>
101 <th>detail</th>
@@ -1,24 +1,24
1 # This file is auto-generated from the current state of the database. Instead of editing this file,
1 # This file is auto-generated from the current state of the database. Instead of editing this file,
2 # please use the migrations feature of ActiveRecord to incrementally modify your database, and
2 # please use the migrations feature of ActiveRecord to incrementally modify your database, and
3 # then regenerate this schema definition.
3 # then regenerate this schema definition.
4 #
4 #
5 # Note that this schema.rb definition is the authoritative source for your database schema. If you need
5 # Note that this schema.rb definition is the authoritative source for your database schema. If you need
6 # to create the application database on another system, you should be using db:schema:load, not running
6 # to create the application database on another system, you should be using db:schema:load, not running
7 # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
7 # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
8 # you'll amass, the slower it'll run and the greater likelihood for issues).
8 # you'll amass, the slower it'll run and the greater likelihood for issues).
9 #
9 #
10 # It's strongly recommended to check this file into your version control system.
10 # It's strongly recommended to check this file into your version control system.
11
11
12 - ActiveRecord::Schema.define(:version => 35) do
12 + ActiveRecord::Schema.define(:version => 36) do
13
13
14 create_table "announcements", :force => true do |t|
14 create_table "announcements", :force => true do |t|
15 t.string "author"
15 t.string "author"
16 t.text "body"
16 t.text "body"
17 t.boolean "published"
17 t.boolean "published"
18 t.datetime "created_at"
18 t.datetime "created_at"
19 t.datetime "updated_at"
19 t.datetime "updated_at"
20 end
20 end
21
21
22 create_table "configurations", :force => true do |t|
22 create_table "configurations", :force => true do |t|
23 t.string "key"
23 t.string "key"
24 t.string "value_type"
24 t.string "value_type"
You need to be logged in to leave comments. Login now