Description:
Merged online-registration branch changes r297:303 into the trunk git-svn-id: http://theory.cpe.ku.ac.th/grader/web/trunk@303 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

r158:13c9210ae5ca - - 16 files changed: 318 inserted, 29 deleted

@@ -0,0 +1,15
1 + - if @result == :successful
2 + %h1 User activated
3 + Your account has been activated.
4 + %br/
5 + Please go ahead to
6 + = link_to 'login.', :controller => 'main', :action => 'login'
7 + - else
8 + %h1 Activation failed
9 + - if @result == :email_used
10 + A user with this E-mail exists.
11 + - else
12 + Your activation code is invalid. Please check again.
13 + %br/
14 + Get back to
15 + = link_to 'home.', :controller => 'main', :action => 'login'
@@ -0,0 +1,9
1 + %h1 Errors in sending registration confirmation
2 +
3 + .errorExplanation
4 + %h2
5 + Your user account has been created, but the system cannot send you the
6 + confirmation e-mail.
7 +
8 + Maybe there's a problem in the configuration. Please report the
9 + admin. Thank you!
@@ -0,0 +1,18
1 + class AddEmailingInfoOnConfigOption < ActiveRecord::Migration
2 + def self.up
3 + # If Configuration['system.online_registration'] is true, the
4 + # system allows online registration, and will use these
5 + # information for sending confirmation emails.
6 + Configuration.create(:key => 'system.online_registration.smtp',
7 + :value_type => 'string',
8 + :value => 'smtp.somehost.com')
9 + Configuration.create(:key => 'system.online_registration.from',
10 + :value_type => 'string',
11 + :value => 'your.email@address')
12 + end
13 +
14 + def self.down
15 + Configuration.find_by_key("system.online_registration.smtp").destroy
16 + Configuration.find_by_key("system.online_registration.from").destroy
17 + end
18 + end
@@ -0,0 +1,9
1 + class AddTimestampsToUsers < ActiveRecord::Migration
2 + def self.up
3 + add_timestamps :users
4 + end
5 +
6 + def self.down
7 + remove_timestamps :users
8 + end
9 + end
@@ -0,0 +1,95
1 +
2 + require File.dirname(__FILE__) + '/../spec_helper'
3 +
4 + describe UsersController, "when a new user registers" do
5 +
6 + before(:each) do
7 + # create john
8 +
9 + @john_info = {:login => 'john',
10 + :full_name => 'John John',
11 + :email => 'john@space.com'}
12 + @john = User.new(@john_info)
13 +
14 + @john_activation_key = "123456"
15 +
16 + @john.should_receive(:activation_key).
17 + any_number_of_times.
18 + and_return(@john_activation_key)
19 +
20 + get :new
21 + response.should render_template('users/new')
22 + end
23 +
24 + it "should show the new form again when user information is invalid" do
25 + User.should_receive(:new).with(any_args()).and_return(@john)
26 + @john.should_receive(:activated=).with(false)
27 + @john.should_receive(:valid?).and_return(false)
28 + @john.should_not_receive(:save)
29 +
30 + post :register, :login => @john_info[:login],
31 + :full_name => @john_info[:full_name],
32 + :email => @john_info[:email]
33 +
34 + response.should render_template('users/new')
35 + end
36 +
37 + it "should create unactivated user and send e-mail with activation key" do
38 + User.should_receive(:new).with(any_args()).and_return(@john)
39 + @john.should_receive(:activated=).with(false)
40 + @john.should_receive(:valid?).and_return(true)
41 + @john.should_receive(:save).and_return(true)
42 +
43 + smtp_mock = mock("smtp")
44 + smtp_mock.should_receive(:send_message) do |msg,fr,to|
45 + to.should == [@john_info[:email]]
46 + msg.index(@john_activation_key).should_not be_nil
47 + end
48 +
49 + Net::SMTP.should_receive(:start).
50 + with(any_args()).
51 + and_yield(smtp_mock)
52 +
53 + post :register, :login => @john_info[:login],
54 + :full_name => @john_info[:full_name],
55 + :email => @john_info[:email]
56 +
57 + response.should render_template('users/new_splash')
58 + end
59 +
60 + it "should create unactivated user and return error page when e-mail sending error" do
61 + User.should_receive(:new).with(any_args()).and_return(@john)
62 + @john.should_receive(:activated=).with(false)
63 + @john.should_receive(:valid?).and_return(true)
64 + @john.should_receive(:save).and_return(true)
65 +
66 + smtp_mock = mock("smtp")
67 + smtp_mock.should_receive(:send_message).
68 + and_throw(:error)
69 +
70 + Net::SMTP.should_receive(:start).
71 + with(any_args()).
72 + and_yield(smtp_mock)
73 +
74 + post :register, :login => @john_info[:login],
75 + :full_name => @john_info[:full_name],
76 + :email => @john_info[:email]
77 +
78 + response.should render_template('users/email_error')
79 + end
80 +
81 + it "should activate user with valid activation key" do
82 + login = @john_info[:login]
83 + User.should_receive(:find_by_login).
84 + with(login).
85 + and_return(@john)
86 + @john.should_receive(:valid?).and_return(true)
87 + @john.should_receive(:activated=).with(true)
88 + @john.should_receive(:save).and_return(true)
89 +
90 + get :confirm, :login => login, :activation => @john_activation_key
91 +
92 + response.should render_template('users/confirm')
93 + end
94 +
95 + end
@@ -1,56 +1,116
1 - require 'pony'
1 + require 'tmail'
2 + require 'net/smtp'
2
3
3 class UsersController < ApplicationController
4 class UsersController < ApplicationController
4
5
5 - before_filter :authenticate, :except => [:new, :register]
6 + before_filter :authenticate, :except => [:new, :register, :confirm]
6
7
7 verify :method => :post, :only => [:chg_passwd],
8 verify :method => :post, :only => [:chg_passwd],
8 :redirect_to => { :action => :index }
9 :redirect_to => { :action => :index }
9
10
10 in_place_edit_for :user, :alias_for_editing
11 in_place_edit_for :user, :alias_for_editing
11 in_place_edit_for :user, :email_for_editing
12 in_place_edit_for :user, :email_for_editing
12
13
13 def index
14 def index
14 if !Configuration['system.user_setting_enabled']
15 if !Configuration['system.user_setting_enabled']
15 redirect_to :controller => 'main', :action => 'list'
16 redirect_to :controller => 'main', :action => 'list'
16 else
17 else
17 @user = User.find(session[:user_id])
18 @user = User.find(session[:user_id])
18 end
19 end
19 end
20 end
20
21
21 def chg_passwd
22 def chg_passwd
22 user = User.find(session[:user_id])
23 user = User.find(session[:user_id])
23 user.password = params[:passwd]
24 user.password = params[:passwd]
24 user.password_confirmation = params[:passwd_verify]
25 user.password_confirmation = params[:passwd_verify]
25 if user.save
26 if user.save
26 flash[:notice] = 'password changed'
27 flash[:notice] = 'password changed'
27 else
28 else
28 flash[:notice] = 'Error: password changing failed'
29 flash[:notice] = 'Error: password changing failed'
29 end
30 end
30 redirect_to :action => 'index'
31 redirect_to :action => 'index'
31 end
32 end
32
33
33 def new
34 def new
34 @user = User.new
35 @user = User.new
35 render :action => 'new', :layout => 'empty'
36 render :action => 'new', :layout => 'empty'
36 end
37 end
37
38
38 def register
39 def register
39 @user = User.new(params[:user])
40 @user = User.new(params[:user])
40 @user.password_confirmation = @user.password = User.random_password
41 @user.password_confirmation = @user.password = User.random_password
41 @user.activated = false
42 @user.activated = false
42 if (@user.valid?) and (@user.save)
43 if (@user.valid?) and (@user.save)
43 - send_confirmation_email(@user)
44 + if send_confirmation_email(@user)
44 - render :action => 'new_splash', :layout => 'empty'
45 + render :action => 'new_splash', :layout => 'empty'
46 + else
47 + render :action => 'email_error', :layout => 'empty'
48 + end
45 else
49 else
46 @user.errors.add_to_base("Email cannot be blank") if @user.email==''
50 @user.errors.add_to_base("Email cannot be blank") if @user.email==''
47 render :action => 'new', :layout => 'empty'
51 render :action => 'new', :layout => 'empty'
48 end
52 end
49 end
53 end
50
54
55 + def confirm
56 + login = params[:login]
57 + key = params[:activation]
58 + user = User.find_by_login(login)
59 + if (user) and (user.verify_activation_key(key))
60 + if user.valid? # check uniquenss of email
61 + user.activated = true
62 + user.save
63 + @result = :successful
64 + else
65 + @result = :email_used
66 + end
67 + else
68 + @result = :failed
69 + end
70 + render :action => 'confirm', :layout => 'empty'
71 + end
72 +
51 protected
73 protected
52
74
53 def send_confirmation_email(user)
75 def send_confirmation_email(user)
76 + contest_name = Configuration['contest.name']
77 + activation_url = url_for(:action => 'confirm',
78 + :login => user.login,
79 + :activation => user.activation_key)
80 + home_url = url_for(:controller => 'main', :action => 'index')
81 + mail = TMail::Mail.new
82 + mail.to = user.email
83 + mail.from = Configuration['system.online_registration.from']
84 + mail.subject = "[#{contest_name}] Confirmation"
85 + mail.body = <<-EOF
86 + Hello #{user.full_name},
87 +
88 + You have registered for #{contest_name} (#{home_url}).
89 +
90 + Your login is: #{user.login}
91 + Your password is: #{user.password}
92 +
93 + Please follow the link:
94 + #{activation_url}
95 + to activate your user account.
96 +
97 + If you did not register, please ignore this e-mail.
98 +
99 + Thanks!
100 + EOF
101 +
102 + smtp_server = Configuration['system.online_registration.smtp']
103 +
104 + begin
105 + Net::SMTP.start(smtp_server) do |smtp|
106 + smtp.send_message(mail.to_s, mail.from, mail.to)
107 + end
108 + result = true
109 + rescue
110 + result = false
111 + end
112 +
113 + return result
54 end
114 end
55
115
56 end
116 end
@@ -15,48 +15,52
15
15
16 def self.get(key)
16 def self.get(key)
17 if @@cache
17 if @@cache
18 if @@configurations == nil
18 if @@configurations == nil
19 self.read_config
19 self.read_config
20 end
20 end
21 return @@configurations[key]
21 return @@configurations[key]
22 else
22 else
23 return Configuration.read_one_key(key)
23 return Configuration.read_one_key(key)
24 end
24 end
25 end
25 end
26
26
27 def self.[](key)
27 def self.[](key)
28 self.get(key)
28 self.get(key)
29 end
29 end
30
30
31 def self.reload
31 def self.reload
32 self.read_config
32 self.read_config
33 end
33 end
34
34
35 def self.clear
35 def self.clear
36 @@configurations = nil
36 @@configurations = nil
37 end
37 end
38
38
39 + def self.enable_caching
40 + @@cache = true
41 + end
42 +
39 #
43 #
40 # View decision
44 # View decision
41 #
45 #
42 def self.show_submitbox_to?(user)
46 def self.show_submitbox_to?(user)
43 mode = get(SYSTEM_MODE_CONF_KEY)
47 mode = get(SYSTEM_MODE_CONF_KEY)
44 return false if mode=='analysis'
48 return false if mode=='analysis'
45 if (mode=='contest')
49 if (mode=='contest')
46 return false if (user.site!=nil) and
50 return false if (user.site!=nil) and
47 ((user.site.started!=true) or (user.site.finished?))
51 ((user.site.started!=true) or (user.site.finished?))
48 end
52 end
49 return true
53 return true
50 end
54 end
51
55
52 def self.show_tasks_to?(user)
56 def self.show_tasks_to?(user)
53 mode = get(SYSTEM_MODE_CONF_KEY)
57 mode = get(SYSTEM_MODE_CONF_KEY)
54 if (mode=='contest')
58 if (mode=='contest')
55 return false if (user.site!=nil) and (user.site.started!=true)
59 return false if (user.site!=nil) and (user.site.started!=true)
56 end
60 end
57 return true
61 return true
58 end
62 end
59
63
60 def self.show_grading_result
64 def self.show_grading_result
61 return (get(SYSTEM_MODE_CONF_KEY)=='analysis')
65 return (get(SYSTEM_MODE_CONF_KEY)=='analysis')
62 end
66 end
@@ -1,62 +1,63
1 require 'digest/sha1'
1 require 'digest/sha1'
2
2
3 class User < ActiveRecord::Base
3 class User < ActiveRecord::Base
4
4
5 has_and_belongs_to_many :roles
5 has_and_belongs_to_many :roles
6
6
7 has_many :test_requests, :order => "submitted_at DESC"
7 has_many :test_requests, :order => "submitted_at DESC"
8
8
9 has_many :messages,
9 has_many :messages,
10 :class_name => "Message",
10 :class_name => "Message",
11 :foreign_key => "sender_id",
11 :foreign_key => "sender_id",
12 :order => 'created_at DESC'
12 :order => 'created_at DESC'
13
13
14 has_many :replied_messages,
14 has_many :replied_messages,
15 :class_name => "Message",
15 :class_name => "Message",
16 :foreign_key => "receiver_id",
16 :foreign_key => "receiver_id",
17 :order => 'created_at DESC'
17 :order => 'created_at DESC'
18
18
19 belongs_to :site
19 belongs_to :site
20 belongs_to :country
20 belongs_to :country
21
21
22 - named_scope :activated, :conditions => {:activated => true}
22 + named_scope :activated_users, :conditions => {:activated => true}
23
23
24 validates_presence_of :login
24 validates_presence_of :login
25 validates_uniqueness_of :login
25 validates_uniqueness_of :login
26 validates_format_of :login, :with => /^[\_a-z0-9]+$/
26 validates_format_of :login, :with => /^[\_a-z0-9]+$/
27 validates_length_of :login, :within => 3..10
27 validates_length_of :login, :within => 3..10
28
28
29 validates_presence_of :full_name
29 validates_presence_of :full_name
30 validates_length_of :full_name, :minimum => 1
30 validates_length_of :full_name, :minimum => 1
31
31
32 validates_presence_of :password, :if => :password_required?
32 validates_presence_of :password, :if => :password_required?
33 validates_length_of :password, :within => 4..20, :if => :password_required?
33 validates_length_of :password, :within => 4..20, :if => :password_required?
34 validates_confirmation_of :password, :if => :password_required?
34 validates_confirmation_of :password, :if => :password_required?
35
35
36 validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :allow_blank => true
36 validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :allow_blank => true
37
37
38 validate :uniqueness_of_email_from_activated_users
38 validate :uniqueness_of_email_from_activated_users
39 + validate :enough_time_interval_between_same_email_registrations
39
40
40 attr_accessor :password
41 attr_accessor :password
41
42
42 before_save :encrypt_new_password
43 before_save :encrypt_new_password
43
44
44 def self.authenticate(login, password)
45 def self.authenticate(login, password)
45 user = find_by_login(login)
46 user = find_by_login(login)
46 return user if user && user.authenticated?(password)
47 return user if user && user.authenticated?(password)
47 end
48 end
48
49
49 def authenticated?(password)
50 def authenticated?(password)
50 if self.activated
51 if self.activated
51 hashed_password == User.encrypt(password,self.salt)
52 hashed_password == User.encrypt(password,self.salt)
52 else
53 else
53 false
54 false
54 end
55 end
55 end
56 end
56
57
57 def admin?
58 def admin?
58 self.roles.detect {|r| r.name == 'admin' }
59 self.roles.detect {|r| r.name == 'admin' }
59 end
60 end
60
61
61 def email_for_editing
62 def email_for_editing
62 if self.email==nil
63 if self.email==nil
@@ -66,59 +67,72
66 else
67 else
67 self.email
68 self.email
68 end
69 end
69 end
70 end
70
71
71 def email_for_editing=(e)
72 def email_for_editing=(e)
72 self.email=e
73 self.email=e
73 end
74 end
74
75
75 def alias_for_editing
76 def alias_for_editing
76 if self.alias==nil
77 if self.alias==nil
77 "(unknown)"
78 "(unknown)"
78 elsif self.alias==''
79 elsif self.alias==''
79 "(blank)"
80 "(blank)"
80 else
81 else
81 self.alias
82 self.alias
82 end
83 end
83 end
84 end
84
85
85 def alias_for_editing=(e)
86 def alias_for_editing=(e)
86 self.alias=e
87 self.alias=e
87 end
88 end
88
89
89 def activation_key
90 def activation_key
91 + if self.hashed_password==nil
92 + encrypt_new_password
93 + end
90 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
94 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
91 end
95 end
92
96
93 def verify_activation_key(key)
97 def verify_activation_key(key)
94 key == activation_key
98 key == activation_key
95 end
99 end
96
100
97 def self.random_password(length=5)
101 def self.random_password(length=5)
98 chars = 'abcdefghjkmnopqrstuvwxyz'
102 chars = 'abcdefghjkmnopqrstuvwxyz'
99 password = ''
103 password = ''
100 length.times { password << chars[rand(chars.length - 1)] }
104 length.times { password << chars[rand(chars.length - 1)] }
101 password
105 password
102 end
106 end
103
107
104 protected
108 protected
105 def encrypt_new_password
109 def encrypt_new_password
106 return if password.blank?
110 return if password.blank?
107 self.salt = (10+rand(90)).to_s
111 self.salt = (10+rand(90)).to_s
108 self.hashed_password = User.encrypt(self.password,self.salt)
112 self.hashed_password = User.encrypt(self.password,self.salt)
109 end
113 end
110
114
111 def password_required?
115 def password_required?
112 self.hashed_password.blank? || !self.password.blank?
116 self.hashed_password.blank? || !self.password.blank?
113 end
117 end
114
118
115 def self.encrypt(string,salt)
119 def self.encrypt(string,salt)
116 Digest::SHA1.hexdigest(salt + string)
120 Digest::SHA1.hexdigest(salt + string)
117 end
121 end
118
122
119 def uniqueness_of_email_from_activated_users
123 def uniqueness_of_email_from_activated_users
120 - if User.activated.find_by_email(self.email)!=nil
124 + user = User.activated_users.find_by_email(self.email)
125 + if user and (user.login != self.login)
121 self.errors.add_to_base("Email has already been taken")
126 self.errors.add_to_base("Email has already been taken")
122 end
127 end
123 end
128 end
129 +
130 + def enough_time_interval_between_same_email_registrations
131 + open_user = User.find_by_email(self.email,
132 + :order => 'created_at DESC')
133 + if open_user and open_user.created_at and
134 + (open_user.created_at > Time.now.gmtime - 5.minutes)
135 + self.errors.add_to_base("There are already unactivated registrations with this e-mail address (please wait for 5 minutes)")
136 + end
137 + end
124 end
138 end
@@ -29,46 +29,49
29 # Use the database for sessions instead of the file system
29 # Use the database for sessions instead of the file system
30 # (create the session table with 'rake db:sessions:create')
30 # (create the session table with 'rake db:sessions:create')
31 config.action_controller.session_store = :active_record_store
31 config.action_controller.session_store = :active_record_store
32
32
33 # Use SQL instead of Active Record's schema dumper when creating the test database.
33 # Use SQL instead of Active Record's schema dumper when creating the test database.
34 # This is necessary if your schema can't be completely dumped by the schema dumper,
34 # This is necessary if your schema can't be completely dumped by the schema dumper,
35 # like if you have constraints or database-specific column types
35 # like if you have constraints or database-specific column types
36 # config.active_record.schema_format = :sql
36 # config.active_record.schema_format = :sql
37
37
38 # Activate observers that should always be running
38 # Activate observers that should always be running
39 # config.active_record.observers = :cacher, :garbage_collector
39 # config.active_record.observers = :cacher, :garbage_collector
40
40
41 # Make Active Record use UTC-base instead of local time
41 # Make Active Record use UTC-base instead of local time
42 config.active_record.default_timezone = :utc
42 config.active_record.default_timezone = :utc
43
43
44 # See Rails::Configuration for more options
44 # See Rails::Configuration for more options
45
45
46 # -------------
46 # -------------
47 # Required gems
47 # Required gems
48 # -------------
48 # -------------
49
49
50 # This is for rspec
50 # This is for rspec
51 config.gem "rspec-rails", :lib => "spec"
51 config.gem "rspec-rails", :lib => "spec"
52 config.gem "haml"
52 config.gem "haml"
53 - config.gem "pony"
53 + config.gem "tmail"
54 #config.gem "BlueCloth", :lig => "bluecloth"
54 #config.gem "BlueCloth", :lig => "bluecloth"
55 end
55 end
56
56
57 # Add new inflection rules using the following format
57 # Add new inflection rules using the following format
58 # (all these examples are active by default):
58 # (all these examples are active by default):
59 # Inflector.inflections do |inflect|
59 # Inflector.inflections do |inflect|
60 # inflect.plural /^(ox)$/i, '\1en'
60 # inflect.plural /^(ox)$/i, '\1en'
61 # inflect.singular /^(ox)en/i, '\1'
61 # inflect.singular /^(ox)en/i, '\1'
62 # inflect.irregular 'person', 'people'
62 # inflect.irregular 'person', 'people'
63 # inflect.uncountable %w( fish sheep )
63 # inflect.uncountable %w( fish sheep )
64 # end
64 # end
65
65
66 # Add new mime types for use in respond_to blocks:
66 # Add new mime types for use in respond_to blocks:
67 # Mime::Type.register "text/richtext", :rtf
67 # Mime::Type.register "text/richtext", :rtf
68 # Mime::Type.register "application/x-mobile", :mobile
68 # Mime::Type.register "application/x-mobile", :mobile
69
69
70 # Include your application configuration below
70 # Include your application configuration below
71
71
72 # These are where inputs and outputs of test requests are stored
72 # These are where inputs and outputs of test requests are stored
73 TEST_REQUEST_INPUT_FILE_DIR = RAILS_ROOT + '/data/test_request/input'
73 TEST_REQUEST_INPUT_FILE_DIR = RAILS_ROOT + '/data/test_request/input'
74 TEST_REQUEST_OUTPUT_FILE_DIR = RAILS_ROOT + '/data/test_request/output'
74 TEST_REQUEST_OUTPUT_FILE_DIR = RAILS_ROOT + '/data/test_request/output'
75 +
76 + # Uncomment so that configuration is read only once when the server is loaded
77 + # Configuration.enable_caching
@@ -1,36 +1,36
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 Active Record to incrementally modify your database, and
2 # please use the migrations feature of Active Record 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 => 20081204122651) do
12 + ActiveRecord::Schema.define(:version => 20081210021333) 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 t.boolean "frontpage", :default => false
20 t.boolean "frontpage", :default => false
21 end
21 end
22
22
23 create_table "configurations", :force => true do |t|
23 create_table "configurations", :force => true do |t|
24 t.string "key"
24 t.string "key"
25 t.string "value_type"
25 t.string "value_type"
26 t.string "value"
26 t.string "value"
27 t.datetime "created_at"
27 t.datetime "created_at"
28 t.datetime "updated_at"
28 t.datetime "updated_at"
29 end
29 end
30
30
31 create_table "countries", :force => true do |t|
31 create_table "countries", :force => true do |t|
32 t.string "name"
32 t.string "name"
33 t.datetime "created_at"
33 t.datetime "created_at"
34 t.datetime "updated_at"
34 t.datetime "updated_at"
35 end
35 end
36
36
@@ -153,38 +153,40
153
153
154 create_table "test_requests", :force => true do |t|
154 create_table "test_requests", :force => true do |t|
155 t.integer "user_id"
155 t.integer "user_id"
156 t.integer "problem_id"
156 t.integer "problem_id"
157 t.integer "submission_id"
157 t.integer "submission_id"
158 t.string "input_file_name"
158 t.string "input_file_name"
159 t.string "output_file_name"
159 t.string "output_file_name"
160 t.string "running_stat"
160 t.string "running_stat"
161 t.integer "status"
161 t.integer "status"
162 t.datetime "updated_at"
162 t.datetime "updated_at"
163 t.datetime "submitted_at"
163 t.datetime "submitted_at"
164 t.datetime "compiled_at"
164 t.datetime "compiled_at"
165 t.text "compiler_message"
165 t.text "compiler_message"
166 t.datetime "graded_at"
166 t.datetime "graded_at"
167 t.string "grader_comment"
167 t.string "grader_comment"
168 t.datetime "created_at"
168 t.datetime "created_at"
169 t.float "running_time"
169 t.float "running_time"
170 t.string "exit_status"
170 t.string "exit_status"
171 t.integer "memory_usage"
171 t.integer "memory_usage"
172 end
172 end
173
173
174 add_index "test_requests", ["user_id", "problem_id"], :name => "index_test_requests_on_user_id_and_problem_id"
174 add_index "test_requests", ["user_id", "problem_id"], :name => "index_test_requests_on_user_id_and_problem_id"
175
175
176 create_table "users", :force => true do |t|
176 create_table "users", :force => true do |t|
177 - t.string "login", :limit => 10
177 + t.string "login", :limit => 10
178 - t.string "full_name"
178 + t.string "full_name"
179 - t.string "hashed_password"
179 + t.string "hashed_password"
180 - t.string "salt", :limit => 5
180 + t.string "salt", :limit => 5
181 - t.string "alias"
181 + t.string "alias"
182 - t.string "email"
182 + t.string "email"
183 - t.integer "site_id"
183 + t.integer "site_id"
184 - t.integer "country_id"
184 + t.integer "country_id"
185 - t.boolean "activated", :default => false
185 + t.boolean "activated", :default => false
186 + t.datetime "created_at"
187 + t.datetime "updated_at"
186 end
188 end
187
189
188 add_index "users", ["login"], :name => "index_users_on_login", :unique => true
190 add_index "users", ["login"], :name => "index_users_on_login", :unique => true
189
191
190 end
192 end
@@ -61,48 +61,56
61 background: #777777;
61 background: #777777;
62 color: white;
62 color: white;
63 }
63 }
64
64
65 tr.info-odd {
65 tr.info-odd {
66 background: #dddddd;
66 background: #dddddd;
67 }
67 }
68
68
69 tr.info-even {
69 tr.info-even {
70 background: #f0f0f0;
70 background: #f0f0f0;
71 }
71 }
72
72
73 /*******************************
73 /*******************************
74 [Main]
74 [Main]
75 ********************************/
75 ********************************/
76 div.submitbox {
76 div.submitbox {
77 border: thin solid black;
77 border: thin solid black;
78 padding: 5px;
78 padding: 5px;
79 color: white;
79 color: white;
80 background-color: #777777;
80 background-color: #777777;
81 font-weight: bold;
81 font-weight: bold;
82 font-size: 13px;
82 font-size: 13px;
83 }
83 }
84
84
85 + div.errorExplanation {
86 + border: 1px dashed black;
87 + color: black;
88 + padding: 5px;
89 + margin-bottom: 5px;
90 + background-color: white;
91 + }
92 +
85 /*******************************
93 /*******************************
86 [Settings]
94 [Settings]
87 ********************************/
95 ********************************/
88 table.uinfo {
96 table.uinfo {
89 border-collapse: collapse;
97 border-collapse: collapse;
90 border: 1px solid black;
98 border: 1px solid black;
91 font-size: 13px;
99 font-size: 13px;
92 }
100 }
93
101
94 td.uinfo {
102 td.uinfo {
95 vertical-align: top;
103 vertical-align: top;
96 border: 1px solid black;
104 border: 1px solid black;
97 padding: 5px;
105 padding: 5px;
98 }
106 }
99
107
100 th.uinfo {
108 th.uinfo {
101 background: lightgreen;
109 background: lightgreen;
102 vertical-align: top;
110 vertical-align: top;
103 text-align: right;
111 text-align: right;
104 border: 1px solid black;
112 border: 1px solid black;
105 padding: 5px;
113 padding: 5px;
106 }
114 }
107
115
108 /*******************************
116 /*******************************
@@ -1,4 +1,5
1 #!/usr/bin/env ruby
1 #!/usr/bin/env ruby
2 $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../vendor/plugins/rspec/lib"))
2 $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../vendor/plugins/rspec/lib"))
3 + require 'rubygems'
3 require 'spec'
4 require 'spec'
4 exit ::Spec::Runner::CommandLine.run(::Spec::Runner::OptionParser.parse(ARGV, STDERR, STDOUT))
5 exit ::Spec::Runner::CommandLine.run(::Spec::Runner::OptionParser.parse(ARGV, STDERR, STDOUT))
@@ -1,50 +1,55
1
1
2 require File.dirname(__FILE__) + '/../spec_helper'
2 require File.dirname(__FILE__) + '/../spec_helper'
3
3
4 describe MainController do
4 describe MainController do
5
5
6 before(:each) do
6 before(:each) do
7 - @problem = mock(Problem, :name => 'test')
7 + @problem = mock(Problem, :name => 'test', :output_only => false)
8 @language = mock(Language, :name => 'cpp', :ext => 'cpp')
8 @language = mock(Language, :name => 'cpp', :ext => 'cpp')
9 @submission = mock(Submission,
9 @submission = mock(Submission,
10 :id => 1,
10 :id => 1,
11 :user_id => 1,
11 :user_id => 1,
12 :problem => @problem,
12 :problem => @problem,
13 :language => @language,
13 :language => @language,
14 :source => 'sample source',
14 :source => 'sample source',
15 :compiler_message => 'none')
15 :compiler_message => 'none')
16 @user = mock(User, :id => 1, :login => 'john')
16 @user = mock(User, :id => 1, :login => 'john')
17 + @another_user = mock(User, :id => 2, :login => 'mary')
17 end
18 end
18
19
19 it "should redirect user to login page when unlogged-in user try to access main/list" do
20 it "should redirect user to login page when unlogged-in user try to access main/list" do
20 get 'list'
21 get 'list'
21 response.should redirect_to(:action => 'login')
22 response.should redirect_to(:action => 'login')
22 end
23 end
23
24
24 it "should let user sees her own source" do
25 it "should let user sees her own source" do
25 Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
26 Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
27 + User.should_receive(:find).with(1).and_return(@user)
26 get 'source', {:id => @submission.id}, {:user_id => 1}
28 get 'source', {:id => @submission.id}, {:user_id => 1}
27 response.should be_success
29 response.should be_success
28 end
30 end
29
31
30 it "should let user sees her own compiler message" do
32 it "should let user sees her own compiler message" do
31 Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
33 Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
34 + User.should_receive(:find).with(1).and_return(@user)
32 get 'compiler_msg', {:id => @submission.id}, {:user_id => 1}
35 get 'compiler_msg', {:id => @submission.id}, {:user_id => 1}
33 response.should be_success
36 response.should be_success
34 end
37 end
35
38
36 it "should not let user sees other user's source" do
39 it "should not let user sees other user's source" do
37 Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
40 Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
41 + User.should_receive(:find).with(2).and_return(@another_user)
38 get 'source', {:id => @submission.id}, {:user_id => 2}
42 get 'source', {:id => @submission.id}, {:user_id => 2}
39 flash[:notice].should =~ /[Ee]rror/
43 flash[:notice].should =~ /[Ee]rror/
40 response.should redirect_to(:action => 'list')
44 response.should redirect_to(:action => 'list')
41 end
45 end
42
46
43 it "should not let user sees other user's compiler message" do
47 it "should not let user sees other user's compiler message" do
44 Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
48 Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
49 + User.should_receive(:find).with(2).and_return(@another_user)
45 get 'compiler_msg', {:id => @submission.id}, {:user_id => 2}
50 get 'compiler_msg', {:id => @submission.id}, {:user_id => 2}
46 flash[:notice].should =~ /[Ee]rror/
51 flash[:notice].should =~ /[Ee]rror/
47 response.should redirect_to(:action => 'list')
52 response.should redirect_to(:action => 'list')
48 end
53 end
49
54
50 end
55 end
@@ -1,28 +1,28
1
1
2 require File.dirname(__FILE__) + '/../spec_helper'
2 require File.dirname(__FILE__) + '/../spec_helper'
3
3
4 describe TestController do
4 describe TestController do
5
5
6 before(:each) do
6 before(:each) do
7 @john = mock(User, :id => "1", :login => 'john')
7 @john = mock(User, :id => "1", :login => 'john')
8 @john_result = mock(TestRequest, :id => "1", :user_id => @john.id)
8 @john_result = mock(TestRequest, :id => "1", :user_id => @john.id)
9 @mary_result = mock(TestRequest, :id => "2", :user_id => @john.id + '1')
9 @mary_result = mock(TestRequest, :id => "2", :user_id => @john.id + '1')
10 - User.should_receive(:find).with(@john.id).and_return(@john)
10 + User.should_receive(:find).at_least(:once).with(@john.id).and_return(@john)
11 end
11 end
12
12
13 it "should let user see her testing result" do
13 it "should let user see her testing result" do
14 TestRequest.should_receive(:find).with(@john_result.id).
14 TestRequest.should_receive(:find).with(@john_result.id).
15 and_return(@john_result)
15 and_return(@john_result)
16 get 'result', {:id => @john_result.id}, {:user_id => @john.id}
16 get 'result', {:id => @john_result.id}, {:user_id => @john.id}
17 response.should be_success
17 response.should be_success
18 end
18 end
19
19
20 it "should not let user see other's testing result" do
20 it "should not let user see other's testing result" do
21 TestRequest.should_receive(:find).with(@mary_result.id).
21 TestRequest.should_receive(:find).with(@mary_result.id).
22 and_return(@mary_result)
22 and_return(@mary_result)
23 get 'result', {:id => @mary_result.id}, {:user_id => @john.id}
23 get 'result', {:id => @mary_result.id}, {:user_id => @john.id}
24 response.should redirect_to(:action => 'index')
24 response.should redirect_to(:action => 'index')
25 end
25 end
26
26
27 end
27 end
28
28
@@ -1,43 +1,48
1
1
2 require File.dirname(__FILE__) + '/../spec_helper'
2 require File.dirname(__FILE__) + '/../spec_helper'
3
3
4 describe Site do
4 describe Site do
5
5
6 before(:each) do
6 before(:each) do
7 - start_time = Time.local(2008,5,10,9,00)
7 + start_time = Time.local(2008,5,10,9,00).gmtime
8 @site = Site.new({:name => 'Test site',
8 @site = Site.new({:name => 'Test site',
9 :started => true,
9 :started => true,
10 :start_time => start_time })
10 :start_time => start_time })
11 + @site.stub!(:start_time).
12 + any_number_of_times.
13 + and_return(start_time)
14 + @site.stub!(:started).any_number_of_times.and_return(true)
11 end
15 end
12
16
13 it "should report that the contest is not finished when the contest time limit is not set" do
17 it "should report that the contest is not finished when the contest time limit is not set" do
14 Configuration.should_receive(:[]).with('contest.time_limit').
18 Configuration.should_receive(:[]).with('contest.time_limit').
15 and_return('unlimit')
19 and_return('unlimit')
16 - Time.should_not_receive(:now)
17 @site.finished?.should == false
20 @site.finished?.should == false
18 end
21 end
19
22
20 it "should report that the contest is finished when the contest is over" do
23 it "should report that the contest is finished when the contest is over" do
21 Configuration.should_receive(:[]).with('contest.time_limit').
24 Configuration.should_receive(:[]).with('contest.time_limit').
22 - and_return('5:00')
25 + and_return('5:00')
23 - Time.should_receive(:now).and_return(Time.local(2008,5,10,14,01))
26 + Time.stub!(:now).
24 - @site.finished?.should == true
27 + and_return(Time.local(2008,5,10,14,01).gmtime)
25 - end
28 + @site.finished?.should == true end
26
29
27 it "should report if the contest is finished correctly, when the contest is over, and the contest time contains some minutes" do
30 it "should report if the contest is finished correctly, when the contest is over, and the contest time contains some minutes" do
28 Configuration.should_receive(:[]).twice.with('contest.time_limit').
31 Configuration.should_receive(:[]).twice.with('contest.time_limit').
29 and_return('5:15')
32 and_return('5:15')
30 - Time.should_receive(:now).
33 + Time.stub!(:now).
31 - and_return(Time.local(2008,5,10,14,14),Time.local(2008,5,10,14,16))
34 + and_return(Time.local(2008,5,10,14,14))
32 @site.finished?.should == false
35 @site.finished?.should == false
36 + Time.stub!(:now).
37 + and_return(Time.local(2008,5,10,14,16))
33 @site.finished?.should == true
38 @site.finished?.should == true
34 end
39 end
35
40
36 it "should report that the contest is not finished, when the time is exactly at the finish time" do
41 it "should report that the contest is not finished, when the time is exactly at the finish time" do
37 Configuration.should_receive(:[]).with('contest.time_limit').
42 Configuration.should_receive(:[]).with('contest.time_limit').
38 and_return('5:00')
43 and_return('5:00')
39 - Time.should_receive(:now).and_return(Time.local(2008,5,10,14,00))
44 + Time.stub!(:now).and_return(Time.local(2008,5,10,14,00))
40 @site.finished?.should == false
45 @site.finished?.should == false
41 end
46 end
42
47
43 end
48 end
@@ -27,37 +27,78
27 end
27 end
28
28
29 end
29 end
30
30
31 describe User, "during registration" do
31 describe User, "during registration" do
32
32
33 class User
33 class User
34 public :encrypt_new_password
34 public :encrypt_new_password
35 end
35 end
36
36
37 before(:each) do
37 before(:each) do
38 @john = User.new(:login => 'john', :password => 'hello')
38 @john = User.new(:login => 'john', :password => 'hello')
39 @john.encrypt_new_password
39 @john.encrypt_new_password
40 end
40 end
41
41
42 it "should produce and accept activation key" do
42 it "should produce and accept activation key" do
43 activation_key = @john.activation_key
43 activation_key = @john.activation_key
44
44
45 @john.verify_activation_key(activation_key).should == true
45 @john.verify_activation_key(activation_key).should == true
46 end
46 end
47
47
48 it "should not accept invalid activation key" do
48 it "should not accept invalid activation key" do
49 @john.verify_activation_key("12345").should == false
49 @john.verify_activation_key("12345").should == false
50 end
50 end
51 -
51 + end
52 +
53 + describe User, "when re-register with the same e-mail" do
54 +
55 + before(:each) do
56 + @mary_email = 'mary@in.th'
57 +
58 + @time_first_register = Time.local(2008,5,10,9,00).gmtime
59 +
60 + @mary_first = mock_model(User,
61 + :login => 'mary1',
62 + :password => 'hello',
63 + :email => @mary_email,
64 + :created_at => @time_first_register)
65 + @mary_second = User.new(:login => 'mary2',
66 + :password => 'hello',
67 + :email => @mary_email)
68 + User.stub!(:find_by_email).
69 + with(@mary_email, {:order => "created_at DESC"}).
70 + and_return(@mary_first)
71 + end
72 +
73 + class User
74 + public :enough_time_interval_between_same_email_registrations
75 + end
76 +
77 + it "should not be allowed if the time interval is less than 5 mins" do
78 + time_now = @time_first_register + 4.minutes
79 + Time.stub!(:now).and_return(time_now)
80 +
81 + @mary_second.enough_time_interval_between_same_email_registrations
82 + @mary_second.errors.length.should_not be_zero
83 + end
84 +
85 + it "should be allowed if the time interval is more than 5 mins" do
86 + time_now = @time_first_register + 6.minutes
87 + Time.stub!(:now).and_return(time_now)
88 +
89 + @mary_second.enough_time_interval_between_same_email_registrations
90 + @mary_second.errors.length.should be_zero
91 + end
92 +
52 end
93 end
53
94
54 describe User, "as a class" do
95 describe User, "as a class" do
55
96
56 it "should be able to generate random password" do
97 it "should be able to generate random password" do
57 password1 = User.random_password
98 password1 = User.random_password
58 password2 = User.random_password
99 password2 = User.random_password
59
100
60 password1.should_not == password2
101 password1.should_not == password2
61 end
102 end
62
103
63 end
104 end
You need to be logged in to leave comments. Login now