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

r840:b948bb241476 - - 9 files changed: 66 inserted, 2 deleted

@@ -0,0 +1,25
1 + = simple_form_for(@announcement) do |f|
2 + - if @announcement.errors.any?
3 + .form-row
4 + .col-sm-12
5 + #error_explanation.alert.alert-dismissable.alert-danger
6 + %button.close{type: "button", data: { dismiss: "alert"}, aria: {hidden: "true"}} ×
7 + %h3= "เกิดปัญหาในการบันทึกข้อมูลเนื่องจาก"
8 + %ul
9 + - @announcement.errors.full_messages.each do |message|
10 + %li= message
11 + .form-row
12 + .col-md-6.col-12
13 + -# = f.input :lock_version, as: :hidden
14 + = f.input :title
15 + = f.input :notes, label: 'Notes (shown internally, used to organize announcements)'
16 + = f.input :body
17 + = f.input :author
18 + = f.input :published
19 + = f.input :frontpage, label: 'Display in the front page only?'
20 + = f.input :on_nav_bar, label: 'Show on menu bar?', wrapper: :custom_boolean
21 + = f.input :contest_only, label: 'Display in contest only?'
22 + = f.submit "Create", class: 'btn btn-primary'
23 + -if content_for?(:form_buttons)
24 + = yield(:form_buttons)
25 + / = link_to 'Back', announcements_path, class: 'btn btn-default'
@@ -0,0 +1,5
1 + class AddOnNavBarToAnnouncement < ActiveRecord::Migration[5.2]
2 + def change
3 + add_column :announcements, :on_nav_bar, :boolean, default: false
4 + end
5 + end
@@ -18,99 +18,99
18 18 # GET /announcements/1
19 19 # GET /announcements/1.xml
20 20 def show
21 21 @announcement = Announcement.find(params[:id])
22 22
23 23 respond_to do |format|
24 24 format.html # show.html.erb
25 25 format.xml { render :xml => @announcement }
26 26 end
27 27 end
28 28
29 29 # GET /announcements/new
30 30 # GET /announcements/new.xml
31 31 def new
32 32 @announcement = Announcement.new
33 33
34 34 respond_to do |format|
35 35 format.html # new.html.erb
36 36 format.xml { render :xml => @announcement }
37 37 end
38 38 end
39 39
40 40 # GET /announcements/1/edit
41 41 def edit
42 42 @announcement = Announcement.find(params[:id])
43 43 end
44 44
45 45 # POST /announcements
46 46 # POST /announcements.xml
47 47 def create
48 48 @announcement = Announcement.new(announcement_params)
49 49
50 50 respond_to do |format|
51 51 if @announcement.save
52 52 flash[:notice] = 'Announcement was successfully created.'
53 53 format.html { redirect_to(@announcement) }
54 54 format.xml { render :xml => @announcement, :status => :created, :location => @announcement }
55 55 else
56 56 format.html { render :action => "new" }
57 57 format.xml { render :xml => @announcement.errors, :status => :unprocessable_entity }
58 58 end
59 59 end
60 60 end
61 61
62 62 # PUT /announcements/1
63 63 # PUT /announcements/1.xml
64 64 def update
65 65 @announcement = Announcement.find(params[:id])
66 66
67 67 respond_to do |format|
68 68 if @announcement.update_attributes(announcement_params)
69 69 flash[:notice] = 'Announcement was successfully updated.'
70 70 format.html { redirect_to(@announcement) }
71 71 format.js {}
72 72 format.xml { head :ok }
73 73 else
74 74 format.html { render :action => "edit" }
75 75 format.js {}
76 76 format.xml { render :xml => @announcement.errors, :status => :unprocessable_entity }
77 77 end
78 78 end
79 79 end
80 80
81 81 def toggle
82 82 @announcement = Announcement.find(params[:id])
83 83 @announcement.update_attributes( published: !@announcement.published? )
84 84 respond_to do |format|
85 85 format.js { render partial: 'toggle_button',
86 86 locals: {button_id: "#announcement_toggle_#{@announcement.id}",button_on: @announcement.published? } }
87 87 end
88 88 end
89 89
90 90 def toggle_front
91 91 @announcement = Announcement.find(params[:id])
92 92 @announcement.update_attributes( frontpage: !@announcement.frontpage? )
93 93 respond_to do |format|
94 94 format.js { render partial: 'toggle_button',
95 95 locals: {button_id: "#announcement_toggle_front_#{@announcement.id}",button_on: @announcement.frontpage? } }
96 96 end
97 97 end
98 98
99 99 # DELETE /announcements/1
100 100 # DELETE /announcements/1.xml
101 101 def destroy
102 102 @announcement = Announcement.find(params[:id])
103 103 @announcement.destroy
104 104
105 105 respond_to do |format|
106 106 format.html { redirect_to(announcements_url) }
107 107 format.xml { head :ok }
108 108 end
109 109 end
110 110
111 111 private
112 112
113 113 def announcement_params
114 - params.require(:announcement).permit(:author, :body, :published, :frontpage, :contest_only, :title)
114 + params.require(:announcement).permit(:author, :body, :published, :frontpage, :contest_only, :title, :on_nav_bar)
115 115 end
116 116 end
@@ -1,120 +1,125
1 1 require 'ipaddr'
2 2
3 3 class ApplicationController < ActionController::Base
4 4 protect_from_forgery
5 5
6 6 before_action :current_user
7 + before_action :nav_announcement
7 8
8 9 SINGLE_USER_MODE_CONF_KEY = 'system.single_user_mode'
9 10 MULTIPLE_IP_LOGIN_CONF_KEY = 'right.multiple_ip_login'
10 11 WHITELIST_IGNORE_CONF_KEY = 'right.whitelist_ignore'
11 12 WHITELIST_IP_CONF_KEY = 'right.whitelist_ip'
12 13
13 14 #report and redirect for unauthorized activities
14 15 def unauthorized_redirect(notice = 'You are not authorized to view the page you requested')
15 16 flash[:notice] = notice
16 17 redirect_to login_main_path
17 18 end
18 19
19 20 # Returns the current logged-in user (if any).
20 21 def current_user
21 22 return nil unless session[:user_id]
22 23 @current_user ||= User.find(session[:user_id])
23 24 end
24 25
26 + def nav_announcement
27 + @nav_announcement = Announcement.where(on_nav_bar: true)
28 + end
29 +
25 30 def admin_authorization
26 31 return false unless check_valid_login
27 32 user = User.includes(:roles).find(session[:user_id])
28 33 unless user.admin?
29 34 unauthorized_redirect
30 35 return false
31 36 end
32 37 return true
33 38 end
34 39
35 40 def authorization_by_roles(allowed_roles)
36 41 return false unless check_valid_login
37 42 unless @current_user.roles.detect { |role| allowed_roles.member?(role.name) }
38 43 unauthorized_redirect
39 44 return false
40 45 end
41 46 end
42 47
43 48 def testcase_authorization
44 49 #admin always has privileged
45 50 if @current_user.admin?
46 51 return true
47 52 end
48 53
49 54 unauthorized_redirect unless GraderConfiguration["right.view_testcase"]
50 55 end
51 56
52 57
53 58 protected
54 59
55 60 #redirect to root (and also force logout)
56 61 #if the user is not logged_in or the system is in "ADMIN ONLY" mode
57 62 def check_valid_login
58 63 #check if logged in
59 64 unless session[:user_id]
60 65 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
61 66 unauthorized_redirect('You need to login but you cannot log in at this time')
62 67 else
63 68 unauthorized_redirect('You need to login')
64 69 end
65 70 return false
66 71 end
67 72
68 73 # check if run in single user mode
69 74 if GraderConfiguration[SINGLE_USER_MODE_CONF_KEY]
70 75 if @current_user==nil || (!@current_user.admin?)
71 76 unauthorized_redirect('You cannot log in at this time')
72 77 return false
73 78 end
74 79 end
75 80
76 81 # check if the user is enabled
77 82 unless @current_user.enabled? || @current_user.admin?
78 83 unauthorized_redirect 'Your account is disabled'
79 84 return false
80 85 end
81 86
82 87 # check if user ip is allowed
83 88 unless @current_user.admin? || GraderConfiguration[WHITELIST_IGNORE_CONF_KEY]
84 89 unless is_request_ip_allowed?
85 90 unauthorized_redirect 'Your IP is not allowed to login at this time.'
86 91 return false
87 92 end
88 93 end
89 94
90 95 if GraderConfiguration.multicontests?
91 96 return true if @current_user.admin?
92 97 begin
93 98 if @current_user.contest_stat(true).forced_logout
94 99 flash[:notice] = 'You have been automatically logged out.'
95 100 redirect_to :controller => 'main', :action => 'index'
96 101 end
97 102 rescue
98 103 end
99 104 end
100 105 return true
101 106 end
102 107
103 108 #redirect to root (and also force logout)
104 109 #if the user use different ip from the previous connection
105 110 # only applicable when MULTIPLE_IP_LOGIN options is false only
106 111 def authenticate_by_ip_address
107 112 #this assume that we have already authenticate normally
108 113 unless GraderConfiguration[MULTIPLE_IP_LOGIN_CONF_KEY]
109 114 user = User.find(session[:user_id])
110 115 if (!user.admin? && user.last_ip && user.last_ip != request.remote_ip)
111 116 flash[:notice] = "You cannot use the system from #{request.remote_ip}. Your last ip is #{user.last_ip}"
112 117 redirect_to :controller => 'main', :action => 'login'
113 118 return false
114 119 end
115 120 unless user.last_ip
116 121 user.last_ip = request.remote_ip
117 122 user.save
118 123 end
119 124 end
120 125 return true
@@ -1,34 +1,45
1 + %h1 Edit Announcement
2 +
3 + -content_for(:form_buttons) do
4 + = link_to t(:back), announcements_path, class: 'card-link btn btn-secondary'
5 +
6 + = render 'form'
7 + -# old style
1 8 .container-fluid
2 9 %h1 Editing announcement
3 10 = error_messages_for :announcement
4 11 .row
5 12 .col-md-6
6 13 = form_for(@announcement) do |f|
7 14 .form-group
8 15 %label Title
9 16 = f.text_field :title, class: 'form-control'
10 17 .form-group
11 18 %label Notes
12 19 (shown internally, used to organize announcements)
13 20 = f.text_field :notes, class: 'form-control'
14 21 .form-group
15 22 %label Body
16 23 = f.text_area :body, class: 'form-control', style: 'height: 200px;'
17 24 .form-group
18 25 %label Author
19 26 = f.text_field :author, class: 'form-control'
20 27 .checkbox
21 28 %label
22 29 = f.check_box :published
23 30 Published
24 31 .checkbox
25 32 %label
26 33 = f.check_box :frontpage
27 34 Show on front page?
28 35 .checkbox
29 36 %label
37 + = f.check_box :on_nav_bar
38 + Show on top menu bar?
39 + .checkbox
40 + %label
30 41 = f.check_box :contest_only
31 42 Show only in contest?
32 43 = f.submit "Update", class: 'btn btn-primary'
33 44 = link_to 'Show', @announcement, class: 'btn btn-default'
34 45 = link_to 'Back', announcements_path, class: 'btn btn-default'
@@ -1,14 +1,22
1 1 %h1 New announcement
2 +
3 + -content_for(:form_buttons) do
4 + = link_to t(:back), announcements_path, class: 'card-link btn btn-secondary'
5 +
6 + = render 'form'
7 +
8 + -# old style
2 9 = error_messages_for :announcement
3 10 = simple_form_for(@announcement) do |f|
4 11 .row
5 12 .col-md-6
6 13 = f.input :title
7 14 = f.input :notes, label: 'Notes (shown internally, used to organize announcements)'
8 15 = f.input :body
9 16 = f.input :author
10 17 = f.input :published
11 18 = f.input :frontpage, label: 'Display in the front page only?'
12 19 = f.input :contest_only, label: 'Display in contest only?'
13 20 = f.button :submit, "Create", class: 'btn btn-primary'
14 21 = link_to 'Back', announcements_path, class: 'btn btn-default'
22 +
@@ -1,24 +1,27
1 1 %p
2 2 %b Author:
3 3 = h @announcement.author
4 4 %p
5 5 %b Title:
6 6 = h @announcement.title
7 7 %p
8 8 %b Notes:
9 9 = h @announcement.notes
10 10 %p
11 11 %b Body:
12 12 = h markdown(@announcement.body)
13 13 %p
14 14 %b Published:
15 15 = h @announcement.published
16 16 %p
17 17 %b Show on front page:
18 18 = h @announcement.frontpage
19 19 %p
20 + %b Show on top menu bar:
21 + = h @announcement.on_nav_bar
22 + %p
20 23 %b Show only in contest:
21 24 = h @announcement.contest_only
22 25 = link_to 'Edit', edit_announcement_path(@announcement)
23 26 |
24 27 = link_to 'Back', announcements_path
@@ -1,96 +1,102
1 1 %header.navbar.navbar-default.navbar-fixed-top
2 2 %nav
3 3 .container-fluid
4 4 .navbar-header
5 5 %button.navbar-toggle.collapsed{ data: {toggle: 'collapse', target: '#navbar-collapse'} }
6 6 %span.sr-only Togggle Navigation
7 7 %span.icon-bar
8 8 %span.icon-bar
9 9 %span.icon-bar
10 10 %a.navbar-brand{href: list_main_path}
11 11 %span.glyphicon.glyphicon-home
12 12 MAIN
13 13 .collapse.navbar-collapse#navbar-collapse
14 14 %ul.nav.navbar-nav
15 15 / submission
16 16 - if (@current_user!=nil) and (GraderConfiguration.show_tasks_to?(@current_user))
17 17 %li.dropdown
18 18 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
19 19 = "#{I18n.t 'menu.submissions'}"
20 20 %span.caret
21 21 %ul.dropdown-menu
22 22 = add_menu("View", 'submissions', 'index')
23 23 = add_menu("Self Test", 'test', 'index')
24 24 / hall of fame
25 25 - if GraderConfiguration['right.user_hall_of_fame']
26 26 = add_menu("#{I18n.t 'menu.hall_of_fame'}", 'report', 'problem_hof')
27 27 / display MODE button (with countdown in contest mode)
28 28 - if GraderConfiguration.analysis_mode?
29 29 %div.navbar-btn.btn.btn-success#countdown= "ANALYSIS MODE"
30 30 - elsif GraderConfiguration.time_limit_mode?
31 31 - if @current_user.contest_finished?
32 32 %div.navbar-btn.btn.btn-danger#countdown= "Contest is over"
33 33 - elsif !@current_user.contest_started?
34 34 %div.navbar-btn.btn.btn-primary#countdown= (t 'title_bar.contest_not_started')
35 35 - else
36 36 %div.navbar-btn.btn.btn-primary#countdown asdf
37 37 :javascript
38 38 $("#countdown").countdown({until: "+#{@current_user.contest_time_left.to_i}s", layout: 'Time left: {hnn}:{mnn}:{snn}'});
39 39 / admin section
40 40 - if (@current_user!=nil) and (session[:admin])
41 41 / management
42 42 %li.dropdown
43 43 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
44 44 Manage
45 45 %span.caret
46 46 %ul.dropdown-menu
47 47 = add_menu( 'Announcements', 'announcements', 'index')
48 48 = add_menu( 'Problems', 'problems', 'index')
49 49 = add_menu( 'Tags', 'tags', 'index')
50 50 = add_menu( 'Users', 'user_admin', 'index')
51 51 = add_menu( 'User Groups', 'groups', 'index')
52 52 = add_menu( 'Graders', 'graders', 'list')
53 53 = add_menu( 'Message ', 'messages', 'console')
54 54 %li.divider{role: 'separator'}
55 55 = add_menu( 'System config', 'configurations', 'index')
56 56 %li.divider{role: 'separator'}
57 57 = add_menu( 'Sites', 'sites', 'index')
58 58 = add_menu( 'Contests', 'contest_management', 'index')
59 59 / report
60 60 %li.dropdown
61 61 %a.dropdown-toggle{href: '#', data: {toggle:'dropdown'}, aria: {haspopup:"true", expanded:"false"}, role: "button"}
62 62 Report
63 63 %span.caret
64 64 %ul.dropdown-menu
65 65 = add_menu( 'Current Score', 'report', 'current_score')
66 66 = add_menu( 'Score Report', 'report', 'max_score')
67 67 = add_menu( 'Submission Report', 'report', 'submission')
68 68 = add_menu( 'Login Report', 'report', 'login')
69 69 - if (ungraded = Submission.where('graded_at is null').where('submitted_at < ?', 1.minutes.ago).count) > 0
70 70 =link_to "#{ungraded} backlogs!",
71 71 graders_list_path,
72 72 class: 'navbar-btn btn btn-default btn-warning', data: {toggle: 'tooltip'},title: 'Number of ungraded submission'
73 + / announcement
74 + - @nav_announcement.each do |ann|
75 + %p.navbar-text
76 + = ann.body.html_safe
77 +
78 +
73 79
74 80 %ul.nav.navbar-nav.navbar-right
75 81 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-question-sign')}".html_safe, 'main', 'help')
76 82 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-comment')}".html_safe, 'messages', 'index', {title: I18n.t('menu.messages'), data: {toggle: 'tooltip'}})
77 83 - if GraderConfiguration['system.user_setting_enabled']
78 84 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-cog', id: 'user_profile')}".html_safe, 'users', 'profile', {title: I18n.t('menu.settings'), data: {toggle: 'tooltip'}})
79 85 = add_menu("#{content_tag(:span,'',class: 'glyphicon glyphicon-log-out')} #{@current_user.full_name}".html_safe, 'main', 'login', {title: I18n.t('menu.log_out'), data: {toggle: 'tooltip'}})
80 86
81 87 /
82 88 - if (@current_user!=nil) and (session[:admin])
83 89 %nav.navbar.navbar-fixed-top.navbar-inverse.secondnavbar
84 90 .container-fluid
85 91 .collapse.navbar-collapse
86 92 %ul.nav.navbar-nav
87 93 = add_menu( '[Announcements]', 'announcements', 'index')
88 94 = add_menu( '[Msg console]', 'messages', 'console')
89 95 = add_menu( '[Problems]', 'problems', 'index')
90 96 = add_menu( '[Users]', 'user_admin', 'index')
91 97 = add_menu( '[Results]', 'user_admin', 'user_stat')
92 98 = add_menu( '[Report]', 'report', 'multiple_login')
93 99 = add_menu( '[Graders]', 'graders', 'list')
94 100 = add_menu( '[Contests]', 'contest_management', 'index')
95 101 = add_menu( '[Sites]', 'sites', 'index')
96 102 = add_menu( '[System config]', 'configurations', 'index')
@@ -1,120 +1,121
1 1 # This file is auto-generated from the current state of the database. Instead
2 2 # of editing this file, please use the migrations feature of Active Record to
3 3 # incrementally modify your database, and then regenerate this schema definition.
4 4 #
5 5 # Note that this schema.rb definition is the authoritative source for your
6 6 # database schema. If you need to create the application database on another
7 7 # system, you should be using db:schema:load, not running all the migrations
8 8 # from scratch. The latter is a flawed and unsustainable approach (the more migrations
9 9 # you'll amass, the slower it'll run and the greater likelihood for issues).
10 10 #
11 11 # It's strongly recommended that you check this file into your version control system.
12 12
13 - ActiveRecord::Schema.define(version: 2020_08_13_083020) do
13 + ActiveRecord::Schema.define(version: 2021_01_24_101028) do
14 14
15 15 create_table "announcements", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
16 16 t.string "author"
17 17 t.text "body"
18 18 t.boolean "published"
19 19 t.datetime "created_at", null: false
20 20 t.datetime "updated_at", null: false
21 21 t.boolean "frontpage", default: false
22 22 t.boolean "contest_only", default: false
23 23 t.string "title"
24 24 t.string "notes"
25 + t.boolean "on_nav_bar", default: false
25 26 end
26 27
27 28 create_table "contests", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
28 29 t.string "title"
29 30 t.boolean "enabled"
30 31 t.datetime "created_at", null: false
31 32 t.datetime "updated_at", null: false
32 33 t.string "name"
33 34 end
34 35
35 36 create_table "contests_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
36 37 t.integer "contest_id"
37 38 t.integer "problem_id"
38 39 end
39 40
40 41 create_table "contests_users", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
41 42 t.integer "contest_id"
42 43 t.integer "user_id"
43 44 end
44 45
45 46 create_table "countries", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
46 47 t.string "name"
47 48 t.datetime "created_at", null: false
48 49 t.datetime "updated_at", null: false
49 50 end
50 51
51 52 create_table "descriptions", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
52 53 t.text "body"
53 54 t.boolean "markdowned"
54 55 t.datetime "created_at", null: false
55 56 t.datetime "updated_at", null: false
56 57 end
57 58
58 59 create_table "grader_configurations", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
59 60 t.string "key"
60 61 t.string "value_type"
61 62 t.string "value"
62 63 t.datetime "created_at", null: false
63 64 t.datetime "updated_at", null: false
64 65 t.text "description"
65 66 end
66 67
67 68 create_table "grader_processes", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
68 69 t.string "host"
69 70 t.integer "pid"
70 71 t.string "mode"
71 72 t.boolean "active"
72 73 t.datetime "created_at", null: false
73 74 t.datetime "updated_at", null: false
74 75 t.integer "task_id"
75 76 t.string "task_type"
76 77 t.boolean "terminated"
77 78 t.index ["host", "pid"], name: "index_grader_processes_on_ip_and_pid"
78 79 end
79 80
80 81 create_table "groups", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
81 82 t.string "name"
82 83 t.string "description"
83 84 t.boolean "enabled", default: true
84 85 end
85 86
86 87 create_table "groups_problems", id: false, options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
87 88 t.integer "problem_id", null: false
88 89 t.integer "group_id", null: false
89 90 t.index ["group_id", "problem_id"], name: "index_groups_problems_on_group_id_and_problem_id"
90 91 end
91 92
92 93 create_table "groups_users", options: "ENGINE=InnoDB DEFAULT CHARSET=latin1", force: :cascade do |t|
93 94 t.integer "group_id", null: false
94 95 t.integer "user_id", null: false
95 96 t.index ["user_id", "group_id"], name: "index_groups_users_on_user_id_and_group_id"
96 97 end
97 98
98 99 create_table "heart_beats", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
99 100 t.integer "user_id"
100 101 t.string "ip_address"
101 102 t.datetime "created_at", null: false
102 103 t.datetime "updated_at", null: false
103 104 t.string "status"
104 105 t.index ["updated_at"], name: "index_heart_beats_on_updated_at"
105 106 end
106 107
107 108 create_table "languages", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
108 109 t.string "name", limit: 10
109 110 t.string "pretty_name"
110 111 t.string "ext", limit: 10
111 112 t.string "common_ext"
112 113 end
113 114
114 115 create_table "logins", id: :integer, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci", force: :cascade do |t|
115 116 t.integer "user_id"
116 117 t.string "ip_address"
117 118 t.datetime "created_at", null: false
118 119 t.datetime "updated_at", null: false
119 120 t.index ["user_id"], name: "index_logins_on_user_id"
120 121 end
You need to be logged in to leave comments. Login now