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,11 +1,12
1 - require 'pony'
1 + require 'tmail'
2 + require 'net/smtp'
2 3
3 4 class UsersController < ApplicationController
4 5
5 - before_filter :authenticate, :except => [:new, :register]
6 + before_filter :authenticate, :except => [:new, :register, :confirm]
6 7
7 8 verify :method => :post, :only => [:chg_passwd],
8 9 :redirect_to => { :action => :index }
9 10
10 11 in_place_edit_for :user, :alias_for_editing
11 12 in_place_edit_for :user, :email_for_editing
@@ -37,20 +38,79
37 38
38 39 def register
39 40 @user = User.new(params[:user])
40 41 @user.password_confirmation = @user.password = User.random_password
41 42 @user.activated = false
42 43 if (@user.valid?) and (@user.save)
43 - send_confirmation_email(@user)
44 - render :action => 'new_splash', :layout => 'empty'
44 + if send_confirmation_email(@user)
45 + render :action => 'new_splash', :layout => 'empty'
46 + else
47 + render :action => 'email_error', :layout => 'empty'
48 + end
45 49 else
46 50 @user.errors.add_to_base("Email cannot be blank") if @user.email==''
47 51 render :action => 'new', :layout => 'empty'
48 52 end
49 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 73 protected
52 74
53 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 114 end
55 115
56 116 end
@@ -33,12 +33,16
33 33 end
34 34
35 35 def self.clear
36 36 @@configurations = nil
37 37 end
38 38
39 + def self.enable_caching
40 + @@cache = true
41 + end
42 +
39 43 #
40 44 # View decision
41 45 #
42 46 def self.show_submitbox_to?(user)
43 47 mode = get(SYSTEM_MODE_CONF_KEY)
44 48 return false if mode=='analysis'
@@ -16,13 +16,13
16 16 :foreign_key => "receiver_id",
17 17 :order => 'created_at DESC'
18 18
19 19 belongs_to :site
20 20 belongs_to :country
21 21
22 - named_scope :activated, :conditions => {:activated => true}
22 + named_scope :activated_users, :conditions => {:activated => true}
23 23
24 24 validates_presence_of :login
25 25 validates_uniqueness_of :login
26 26 validates_format_of :login, :with => /^[\_a-z0-9]+$/
27 27 validates_length_of :login, :within => 3..10
28 28
@@ -33,12 +33,13
33 33 validates_length_of :password, :within => 4..20, :if => :password_required?
34 34 validates_confirmation_of :password, :if => :password_required?
35 35
36 36 validates_format_of :email, :with => /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i, :allow_blank => true
37 37
38 38 validate :uniqueness_of_email_from_activated_users
39 + validate :enough_time_interval_between_same_email_registrations
39 40
40 41 attr_accessor :password
41 42
42 43 before_save :encrypt_new_password
43 44
44 45 def self.authenticate(login, password)
@@ -84,12 +85,15
84 85
85 86 def alias_for_editing=(e)
86 87 self.alias=e
87 88 end
88 89
89 90 def activation_key
91 + if self.hashed_password==nil
92 + encrypt_new_password
93 + end
90 94 Digest::SHA1.hexdigest(self.hashed_password)[0..7]
91 95 end
92 96
93 97 def verify_activation_key(key)
94 98 key == activation_key
95 99 end
@@ -114,11 +118,21
114 118
115 119 def self.encrypt(string,salt)
116 120 Digest::SHA1.hexdigest(salt + string)
117 121 end
118 122
119 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 126 self.errors.add_to_base("Email has already been taken")
122 127 end
123 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 138 end
@@ -47,13 +47,13
47 47 # Required gems
48 48 # -------------
49 49
50 50 # This is for rspec
51 51 config.gem "rspec-rails", :lib => "spec"
52 52 config.gem "haml"
53 - config.gem "pony"
53 + config.gem "tmail"
54 54 #config.gem "BlueCloth", :lig => "bluecloth"
55 55 end
56 56
57 57 # Add new inflection rules using the following format
58 58 # (all these examples are active by default):
59 59 # Inflector.inflections do |inflect|
@@ -69,6 +69,9
69 69
70 70 # Include your application configuration below
71 71
72 72 # These are where inputs and outputs of test requests are stored
73 73 TEST_REQUEST_INPUT_FILE_DIR = RAILS_ROOT + '/data/test_request/input'
74 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
@@ -6,13 +6,13
6 6 # to create the application database on another system, you should be using db:schema:load, not running
7 7 # all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
8 8 # you'll amass, the slower it'll run and the greater likelihood for issues).
9 9 #
10 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 14 create_table "announcements", :force => true do |t|
15 15 t.string "author"
16 16 t.text "body"
17 17 t.boolean "published"
18 18 t.datetime "created_at"
@@ -171,20 +171,22
171 171 t.integer "memory_usage"
172 172 end
173 173
174 174 add_index "test_requests", ["user_id", "problem_id"], :name => "index_test_requests_on_user_id_and_problem_id"
175 175
176 176 create_table "users", :force => true do |t|
177 - t.string "login", :limit => 10
178 - t.string "full_name"
179 - t.string "hashed_password"
180 - t.string "salt", :limit => 5
181 - t.string "alias"
182 - t.string "email"
183 - t.integer "site_id"
184 - t.integer "country_id"
185 - t.boolean "activated", :default => false
177 + t.string "login", :limit => 10
178 + t.string "full_name"
179 + t.string "hashed_password"
180 + t.string "salt", :limit => 5
181 + t.string "alias"
182 + t.string "email"
183 + t.integer "site_id"
184 + t.integer "country_id"
185 + t.boolean "activated", :default => false
186 + t.datetime "created_at"
187 + t.datetime "updated_at"
186 188 end
187 189
188 190 add_index "users", ["login"], :name => "index_users_on_login", :unique => true
189 191
190 192 end
@@ -79,12 +79,20
79 79 color: white;
80 80 background-color: #777777;
81 81 font-weight: bold;
82 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 94 [Settings]
87 95 ********************************/
88 96 table.uinfo {
89 97 border-collapse: collapse;
90 98 border: 1px solid black;
@@ -1,4 +1,5
1 1 #!/usr/bin/env ruby
2 2 $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../vendor/plugins/rspec/lib"))
3 + require 'rubygems'
3 4 require 'spec'
4 5 exit ::Spec::Runner::CommandLine.run(::Spec::Runner::OptionParser.parse(ARGV, STDERR, STDOUT))
@@ -1,50 +1,55
1 1
2 2 require File.dirname(__FILE__) + '/../spec_helper'
3 3
4 4 describe MainController do
5 5
6 6 before(:each) do
7 - @problem = mock(Problem, :name => 'test')
7 + @problem = mock(Problem, :name => 'test', :output_only => false)
8 8 @language = mock(Language, :name => 'cpp', :ext => 'cpp')
9 9 @submission = mock(Submission,
10 10 :id => 1,
11 11 :user_id => 1,
12 12 :problem => @problem,
13 13 :language => @language,
14 14 :source => 'sample source',
15 15 :compiler_message => 'none')
16 16 @user = mock(User, :id => 1, :login => 'john')
17 + @another_user = mock(User, :id => 2, :login => 'mary')
17 18 end
18 19
19 20 it "should redirect user to login page when unlogged-in user try to access main/list" do
20 21 get 'list'
21 22 response.should redirect_to(:action => 'login')
22 23 end
23 24
24 25 it "should let user sees her own source" do
25 26 Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
27 + User.should_receive(:find).with(1).and_return(@user)
26 28 get 'source', {:id => @submission.id}, {:user_id => 1}
27 29 response.should be_success
28 30 end
29 31
30 32 it "should let user sees her own compiler message" do
31 33 Submission.should_receive(:find).with(@submission.id.to_s).and_return(@submission)
34 + User.should_receive(:find).with(1).and_return(@user)
32 35 get 'compiler_msg', {:id => @submission.id}, {:user_id => 1}
33 36 response.should be_success
34 37 end
35 38
36 39 it "should not let user sees other user's source" do
37 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 42 get 'source', {:id => @submission.id}, {:user_id => 2}
39 43 flash[:notice].should =~ /[Ee]rror/
40 44 response.should redirect_to(:action => 'list')
41 45 end
42 46
43 47 it "should not let user sees other user's compiler message" do
44 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 50 get 'compiler_msg', {:id => @submission.id}, {:user_id => 2}
46 51 flash[:notice].should =~ /[Ee]rror/
47 52 response.should redirect_to(:action => 'list')
48 53 end
49 54
50 55 end
@@ -4,13 +4,13
4 4 describe TestController do
5 5
6 6 before(:each) do
7 7 @john = mock(User, :id => "1", :login => 'john')
8 8 @john_result = mock(TestRequest, :id => "1", :user_id => @john.id)
9 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 11 end
12 12
13 13 it "should let user see her testing result" do
14 14 TestRequest.should_receive(:find).with(@john_result.id).
15 15 and_return(@john_result)
16 16 get 'result', {:id => @john_result.id}, {:user_id => @john.id}
@@ -1,43 +1,48
1 1
2 2 require File.dirname(__FILE__) + '/../spec_helper'
3 3
4 4 describe Site do
5 5
6 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 8 @site = Site.new({:name => 'Test site',
9 9 :started => true,
10 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 15 end
12 16
13 17 it "should report that the contest is not finished when the contest time limit is not set" do
14 18 Configuration.should_receive(:[]).with('contest.time_limit').
15 19 and_return('unlimit')
16 - Time.should_not_receive(:now)
17 20 @site.finished?.should == false
18 21 end
19 22
20 23 it "should report that the contest is finished when the contest is over" do
21 24 Configuration.should_receive(:[]).with('contest.time_limit').
22 - and_return('5:00')
23 - Time.should_receive(:now).and_return(Time.local(2008,5,10,14,01))
24 - @site.finished?.should == true
25 - end
25 + and_return('5:00')
26 + Time.stub!(:now).
27 + and_return(Time.local(2008,5,10,14,01).gmtime)
28 + @site.finished?.should == true end
26 29
27 30 it "should report if the contest is finished correctly, when the contest is over, and the contest time contains some minutes" do
28 31 Configuration.should_receive(:[]).twice.with('contest.time_limit').
29 32 and_return('5:15')
30 - Time.should_receive(:now).
31 - and_return(Time.local(2008,5,10,14,14),Time.local(2008,5,10,14,16))
33 + Time.stub!(:now).
34 + and_return(Time.local(2008,5,10,14,14))
32 35 @site.finished?.should == false
36 + Time.stub!(:now).
37 + and_return(Time.local(2008,5,10,14,16))
33 38 @site.finished?.should == true
34 39 end
35 40
36 41 it "should report that the contest is not finished, when the time is exactly at the finish time" do
37 42 Configuration.should_receive(:[]).with('contest.time_limit').
38 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 45 @site.finished?.should == false
41 46 end
42 47
43 48 end
@@ -45,13 +45,54
45 45 @john.verify_activation_key(activation_key).should == true
46 46 end
47 47
48 48 it "should not accept invalid activation key" do
49 49 @john.verify_activation_key("12345").should == false
50 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 93 end
53 94
54 95 describe User, "as a class" do
55 96
56 97 it "should be able to generate random password" do
57 98 password1 = User.random_password
You need to be logged in to leave comments. Login now