##// END OF EJS Templates
Refactor: move method to model...
Eric Davis -
r4110:747b9ec5683f
parent child
Show More
@@ -1,185 +1,179
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class MyController < ApplicationController
18 class MyController < ApplicationController
19 before_filter :require_login
19 before_filter :require_login
20
20
21 helper :issues
21 helper :issues
22 helper :custom_fields
22 helper :custom_fields
23
23
24 BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues,
24 BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues,
25 'issuesreportedbyme' => :label_reported_issues,
25 'issuesreportedbyme' => :label_reported_issues,
26 'issueswatched' => :label_watched_issues,
26 'issueswatched' => :label_watched_issues,
27 'news' => :label_news_latest,
27 'news' => :label_news_latest,
28 'calendar' => :label_calendar,
28 'calendar' => :label_calendar,
29 'documents' => :label_document_plural,
29 'documents' => :label_document_plural,
30 'timelog' => :label_spent_time
30 'timelog' => :label_spent_time
31 }.merge(Redmine::Views::MyPage::Block.additional_blocks).freeze
31 }.merge(Redmine::Views::MyPage::Block.additional_blocks).freeze
32
32
33 DEFAULT_LAYOUT = { 'left' => ['issuesassignedtome'],
33 DEFAULT_LAYOUT = { 'left' => ['issuesassignedtome'],
34 'right' => ['issuesreportedbyme']
34 'right' => ['issuesreportedbyme']
35 }.freeze
35 }.freeze
36
36
37 verify :xhr => true,
37 verify :xhr => true,
38 :only => [:add_block, :remove_block, :order_blocks]
38 :only => [:add_block, :remove_block, :order_blocks]
39
39
40 def index
40 def index
41 page
41 page
42 render :action => 'page'
42 render :action => 'page'
43 end
43 end
44
44
45 # Show user's page
45 # Show user's page
46 def page
46 def page
47 @user = User.current
47 @user = User.current
48 @blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT
48 @blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT
49 end
49 end
50
50
51 # Edit user's account
51 # Edit user's account
52 def account
52 def account
53 @user = User.current
53 @user = User.current
54 @pref = @user.pref
54 @pref = @user.pref
55 if request.post?
55 if request.post?
56 @user.attributes = params[:user]
56 @user.attributes = params[:user]
57 @user.mail_notification = params[:notification_option] || 'only_my_events'
57 @user.mail_notification = params[:notification_option] || 'only_my_events'
58 @user.pref.attributes = params[:pref]
58 @user.pref.attributes = params[:pref]
59 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
59 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
60 if @user.save
60 if @user.save
61 @user.pref.save
61 @user.pref.save
62 @user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
62 @user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
63 set_language_if_valid @user.language
63 set_language_if_valid @user.language
64 flash[:notice] = l(:notice_account_updated)
64 flash[:notice] = l(:notice_account_updated)
65 redirect_to :action => 'account'
65 redirect_to :action => 'account'
66 return
66 return
67 end
67 end
68 end
68 end
69 @notification_options = User::MAIL_NOTIFICATION_OPTIONS
69 @notification_options = @user.valid_notification_options
70 # Only users that belong to more than 1 project can select projects for which they are notified
71 # Note that @user.membership.size would fail since AR ignores
72 # :include association option when doing a count
73 if @user.memberships.length < 1
74 @notification_options.delete_if {|option| option.first == :selected}
75 end
76 @notification_option = @user.mail_notification #? ? 'all' : (@user.notified_projects_ids.empty? ? 'none' : 'selected')
70 @notification_option = @user.mail_notification #? ? 'all' : (@user.notified_projects_ids.empty? ? 'none' : 'selected')
77 end
71 end
78
72
79 # Manage user's password
73 # Manage user's password
80 def password
74 def password
81 @user = User.current
75 @user = User.current
82 unless @user.change_password_allowed?
76 unless @user.change_password_allowed?
83 flash[:error] = l(:notice_can_t_change_password)
77 flash[:error] = l(:notice_can_t_change_password)
84 redirect_to :action => 'account'
78 redirect_to :action => 'account'
85 return
79 return
86 end
80 end
87 if request.post?
81 if request.post?
88 if @user.check_password?(params[:password])
82 if @user.check_password?(params[:password])
89 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
83 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
90 if @user.save
84 if @user.save
91 flash[:notice] = l(:notice_account_password_updated)
85 flash[:notice] = l(:notice_account_password_updated)
92 redirect_to :action => 'account'
86 redirect_to :action => 'account'
93 end
87 end
94 else
88 else
95 flash[:error] = l(:notice_account_wrong_password)
89 flash[:error] = l(:notice_account_wrong_password)
96 end
90 end
97 end
91 end
98 end
92 end
99
93
100 # Create a new feeds key
94 # Create a new feeds key
101 def reset_rss_key
95 def reset_rss_key
102 if request.post?
96 if request.post?
103 if User.current.rss_token
97 if User.current.rss_token
104 User.current.rss_token.destroy
98 User.current.rss_token.destroy
105 User.current.reload
99 User.current.reload
106 end
100 end
107 User.current.rss_key
101 User.current.rss_key
108 flash[:notice] = l(:notice_feeds_access_key_reseted)
102 flash[:notice] = l(:notice_feeds_access_key_reseted)
109 end
103 end
110 redirect_to :action => 'account'
104 redirect_to :action => 'account'
111 end
105 end
112
106
113 # Create a new API key
107 # Create a new API key
114 def reset_api_key
108 def reset_api_key
115 if request.post?
109 if request.post?
116 if User.current.api_token
110 if User.current.api_token
117 User.current.api_token.destroy
111 User.current.api_token.destroy
118 User.current.reload
112 User.current.reload
119 end
113 end
120 User.current.api_key
114 User.current.api_key
121 flash[:notice] = l(:notice_api_access_key_reseted)
115 flash[:notice] = l(:notice_api_access_key_reseted)
122 end
116 end
123 redirect_to :action => 'account'
117 redirect_to :action => 'account'
124 end
118 end
125
119
126 # User's page layout configuration
120 # User's page layout configuration
127 def page_layout
121 def page_layout
128 @user = User.current
122 @user = User.current
129 @blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT.dup
123 @blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT.dup
130 @block_options = []
124 @block_options = []
131 BLOCKS.each {|k, v| @block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize]}
125 BLOCKS.each {|k, v| @block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize]}
132 end
126 end
133
127
134 # Add a block to user's page
128 # Add a block to user's page
135 # The block is added on top of the page
129 # The block is added on top of the page
136 # params[:block] : id of the block to add
130 # params[:block] : id of the block to add
137 def add_block
131 def add_block
138 block = params[:block].to_s.underscore
132 block = params[:block].to_s.underscore
139 (render :nothing => true; return) unless block && (BLOCKS.keys.include? block)
133 (render :nothing => true; return) unless block && (BLOCKS.keys.include? block)
140 @user = User.current
134 @user = User.current
141 layout = @user.pref[:my_page_layout] || {}
135 layout = @user.pref[:my_page_layout] || {}
142 # remove if already present in a group
136 # remove if already present in a group
143 %w(top left right).each {|f| (layout[f] ||= []).delete block }
137 %w(top left right).each {|f| (layout[f] ||= []).delete block }
144 # add it on top
138 # add it on top
145 layout['top'].unshift block
139 layout['top'].unshift block
146 @user.pref[:my_page_layout] = layout
140 @user.pref[:my_page_layout] = layout
147 @user.pref.save
141 @user.pref.save
148 render :partial => "block", :locals => {:user => @user, :block_name => block}
142 render :partial => "block", :locals => {:user => @user, :block_name => block}
149 end
143 end
150
144
151 # Remove a block to user's page
145 # Remove a block to user's page
152 # params[:block] : id of the block to remove
146 # params[:block] : id of the block to remove
153 def remove_block
147 def remove_block
154 block = params[:block].to_s.underscore
148 block = params[:block].to_s.underscore
155 @user = User.current
149 @user = User.current
156 # remove block in all groups
150 # remove block in all groups
157 layout = @user.pref[:my_page_layout] || {}
151 layout = @user.pref[:my_page_layout] || {}
158 %w(top left right).each {|f| (layout[f] ||= []).delete block }
152 %w(top left right).each {|f| (layout[f] ||= []).delete block }
159 @user.pref[:my_page_layout] = layout
153 @user.pref[:my_page_layout] = layout
160 @user.pref.save
154 @user.pref.save
161 render :nothing => true
155 render :nothing => true
162 end
156 end
163
157
164 # Change blocks order on user's page
158 # Change blocks order on user's page
165 # params[:group] : group to order (top, left or right)
159 # params[:group] : group to order (top, left or right)
166 # params[:list-(top|left|right)] : array of block ids of the group
160 # params[:list-(top|left|right)] : array of block ids of the group
167 def order_blocks
161 def order_blocks
168 group = params[:group]
162 group = params[:group]
169 @user = User.current
163 @user = User.current
170 if group.is_a?(String)
164 if group.is_a?(String)
171 group_items = (params["list-#{group}"] || []).collect(&:underscore)
165 group_items = (params["list-#{group}"] || []).collect(&:underscore)
172 if group_items and group_items.is_a? Array
166 if group_items and group_items.is_a? Array
173 layout = @user.pref[:my_page_layout] || {}
167 layout = @user.pref[:my_page_layout] || {}
174 # remove group blocks if they are presents in other groups
168 # remove group blocks if they are presents in other groups
175 %w(top left right).each {|f|
169 %w(top left right).each {|f|
176 layout[f] = (layout[f] || []) - group_items
170 layout[f] = (layout[f] || []) - group_items
177 }
171 }
178 layout[group] = group_items
172 layout[group] = group_items
179 @user.pref[:my_page_layout] = layout
173 @user.pref[:my_page_layout] = layout
180 @user.pref.save
174 @user.pref.save
181 end
175 end
182 end
176 end
183 render :nothing => true
177 render :nothing => true
184 end
178 end
185 end
179 end
@@ -1,202 +1,187
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class UsersController < ApplicationController
18 class UsersController < ApplicationController
19 layout 'admin'
19 layout 'admin'
20
20
21 before_filter :require_admin, :except => :show
21 before_filter :require_admin, :except => :show
22
22
23 helper :sort
23 helper :sort
24 include SortHelper
24 include SortHelper
25 helper :custom_fields
25 helper :custom_fields
26 include CustomFieldsHelper
26 include CustomFieldsHelper
27
27
28 def index
28 def index
29 sort_init 'login', 'asc'
29 sort_init 'login', 'asc'
30 sort_update %w(login firstname lastname mail admin created_on last_login_on)
30 sort_update %w(login firstname lastname mail admin created_on last_login_on)
31
31
32 @status = params[:status] ? params[:status].to_i : 1
32 @status = params[:status] ? params[:status].to_i : 1
33 c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
33 c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
34
34
35 unless params[:name].blank?
35 unless params[:name].blank?
36 name = "%#{params[:name].strip.downcase}%"
36 name = "%#{params[:name].strip.downcase}%"
37 c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ? OR LOWER(mail) LIKE ?", name, name, name, name]
37 c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ? OR LOWER(mail) LIKE ?", name, name, name, name]
38 end
38 end
39
39
40 @user_count = User.count(:conditions => c.conditions)
40 @user_count = User.count(:conditions => c.conditions)
41 @user_pages = Paginator.new self, @user_count,
41 @user_pages = Paginator.new self, @user_count,
42 per_page_option,
42 per_page_option,
43 params['page']
43 params['page']
44 @users = User.find :all,:order => sort_clause,
44 @users = User.find :all,:order => sort_clause,
45 :conditions => c.conditions,
45 :conditions => c.conditions,
46 :limit => @user_pages.items_per_page,
46 :limit => @user_pages.items_per_page,
47 :offset => @user_pages.current.offset
47 :offset => @user_pages.current.offset
48
48
49 render :layout => !request.xhr?
49 render :layout => !request.xhr?
50 end
50 end
51
51
52 def show
52 def show
53 @user = User.find(params[:id])
53 @user = User.find(params[:id])
54 @custom_values = @user.custom_values
54 @custom_values = @user.custom_values
55
55
56 # show projects based on current user visibility
56 # show projects based on current user visibility
57 @memberships = @user.memberships.all(:conditions => Project.visible_by(User.current))
57 @memberships = @user.memberships.all(:conditions => Project.visible_by(User.current))
58
58
59 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
59 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
60 @events_by_day = events.group_by(&:event_date)
60 @events_by_day = events.group_by(&:event_date)
61
61
62 unless User.current.admin?
62 unless User.current.admin?
63 if !@user.active? || (@user != User.current && @memberships.empty? && events.empty?)
63 if !@user.active? || (@user != User.current && @memberships.empty? && events.empty?)
64 render_404
64 render_404
65 return
65 return
66 end
66 end
67 end
67 end
68 render :layout => 'base'
68 render :layout => 'base'
69
69
70 rescue ActiveRecord::RecordNotFound
70 rescue ActiveRecord::RecordNotFound
71 render_404
71 render_404
72 end
72 end
73
73
74 def add
74 def add
75 @notification_options = User::MAIL_NOTIFICATION_OPTIONS
75 @notification_options = User::MAIL_NOTIFICATION_OPTIONS
76 @notification_option = Setting.default_notification_option
76 @notification_option = Setting.default_notification_option
77
77
78 @user = User.new(:language => Setting.default_language)
78 @user = User.new(:language => Setting.default_language)
79 @auth_sources = AuthSource.find(:all)
79 @auth_sources = AuthSource.find(:all)
80
81 # TODO: Similar to My#account
82 # Only users that belong to more than 1 project can select projects for which they are notified
83 # Note that @user.membership.size would fail since AR ignores
84 # :include association option when doing a count
85 if @user.memberships.length < 1
86 @notification_options.delete_if {|option| option.first == :selected}
87 end
88 end
80 end
89
81
90 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
82 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
91 def create
83 def create
92 @notification_options = User::MAIL_NOTIFICATION_OPTIONS
84 @notification_options = User::MAIL_NOTIFICATION_OPTIONS
93 @notification_option = Setting.default_notification_option
85 @notification_option = Setting.default_notification_option
94
86
95 @user = User.new(params[:user])
87 @user = User.new(params[:user])
96 @user.admin = params[:user][:admin] || false
88 @user.admin = params[:user][:admin] || false
97 @user.login = params[:user][:login]
89 @user.login = params[:user][:login]
98 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless @user.auth_source_id
90 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless @user.auth_source_id
99
91
100 # TODO: Similar to My#account
92 # TODO: Similar to My#account
101 @user.mail_notification = params[:notification_option] || 'only_my_events'
93 @user.mail_notification = params[:notification_option] || 'only_my_events'
102 @user.pref.attributes = params[:pref]
94 @user.pref.attributes = params[:pref]
103 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
95 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
104
96
105 if @user.save
97 if @user.save
106 @user.pref.save
98 @user.pref.save
107 @user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
99 @user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
108
100
109 Mailer.deliver_account_information(@user, params[:password]) if params[:send_information]
101 Mailer.deliver_account_information(@user, params[:password]) if params[:send_information]
110 flash[:notice] = l(:notice_successful_create)
102 flash[:notice] = l(:notice_successful_create)
111 redirect_to(params[:continue] ? {:controller => 'users', :action => 'add'} :
103 redirect_to(params[:continue] ? {:controller => 'users', :action => 'add'} :
112 {:controller => 'users', :action => 'edit', :id => @user})
104 {:controller => 'users', :action => 'edit', :id => @user})
113 return
105 return
114 else
106 else
115 @auth_sources = AuthSource.find(:all)
107 @auth_sources = AuthSource.find(:all)
116 @notification_option = @user.mail_notification
108 @notification_option = @user.mail_notification
117
109
118 render :action => 'add'
110 render :action => 'add'
119 end
111 end
120 end
112 end
121
113
122 def edit
114 def edit
123 @user = User.find(params[:id])
115 @user = User.find(params[:id])
124 # TODO: Similar to My#account
116 @notification_options = @user.valid_notification_options
125 @notification_options = User::MAIL_NOTIFICATION_OPTIONS
126 # Only users that belong to more than 1 project can select projects for which they are notified
127 # Note that @user.membership.size would fail since AR ignores
128 # :include association option when doing a count
129 if @user.memberships.length < 1
130 @notification_options.delete_if {|option| option.first == :selected}
131 end
132 @notification_option = @user.mail_notification
117 @notification_option = @user.mail_notification
133
118
134 if request.post?
119 if request.post?
135 @user.admin = params[:user][:admin] if params[:user][:admin]
120 @user.admin = params[:user][:admin] if params[:user][:admin]
136 @user.login = params[:user][:login] if params[:user][:login]
121 @user.login = params[:user][:login] if params[:user][:login]
137 if params[:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
122 if params[:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
138 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
123 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
139 end
124 end
140 @user.group_ids = params[:user][:group_ids] if params[:user][:group_ids]
125 @user.group_ids = params[:user][:group_ids] if params[:user][:group_ids]
141 @user.attributes = params[:user]
126 @user.attributes = params[:user]
142 # Was the account actived ? (do it before User#save clears the change)
127 # Was the account actived ? (do it before User#save clears the change)
143 was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
128 was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
144 # TODO: Similar to My#account
129 # TODO: Similar to My#account
145 @user.mail_notification = params[:notification_option] || 'only_my_events'
130 @user.mail_notification = params[:notification_option] || 'only_my_events'
146 @user.pref.attributes = params[:pref]
131 @user.pref.attributes = params[:pref]
147 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
132 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
148
133
149 if @user.save
134 if @user.save
150 @user.pref.save
135 @user.pref.save
151 @user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
136 @user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
152
137
153 if was_activated
138 if was_activated
154 Mailer.deliver_account_activated(@user)
139 Mailer.deliver_account_activated(@user)
155 elsif @user.active? && params[:send_information] && !params[:password].blank? && @user.auth_source_id.nil?
140 elsif @user.active? && params[:send_information] && !params[:password].blank? && @user.auth_source_id.nil?
156 Mailer.deliver_account_information(@user, params[:password])
141 Mailer.deliver_account_information(@user, params[:password])
157 end
142 end
158 flash[:notice] = l(:notice_successful_update)
143 flash[:notice] = l(:notice_successful_update)
159 redirect_to :back
144 redirect_to :back
160 end
145 end
161 end
146 end
162 @auth_sources = AuthSource.find(:all)
147 @auth_sources = AuthSource.find(:all)
163 @membership ||= Member.new
148 @membership ||= Member.new
164 rescue ::ActionController::RedirectBackError
149 rescue ::ActionController::RedirectBackError
165 redirect_to :controller => 'users', :action => 'edit', :id => @user
150 redirect_to :controller => 'users', :action => 'edit', :id => @user
166 end
151 end
167
152
168 def edit_membership
153 def edit_membership
169 @user = User.find(params[:id])
154 @user = User.find(params[:id])
170 @membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
155 @membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
171 @membership.save if request.post?
156 @membership.save if request.post?
172 respond_to do |format|
157 respond_to do |format|
173 if @membership.valid?
158 if @membership.valid?
174 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
159 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
175 format.js {
160 format.js {
176 render(:update) {|page|
161 render(:update) {|page|
177 page.replace_html "tab-content-memberships", :partial => 'users/memberships'
162 page.replace_html "tab-content-memberships", :partial => 'users/memberships'
178 page.visual_effect(:highlight, "member-#{@membership.id}")
163 page.visual_effect(:highlight, "member-#{@membership.id}")
179 }
164 }
180 }
165 }
181 else
166 else
182 format.js {
167 format.js {
183 render(:update) {|page|
168 render(:update) {|page|
184 page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')))
169 page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')))
185 }
170 }
186 }
171 }
187 end
172 end
188 end
173 end
189 end
174 end
190
175
191 def destroy_membership
176 def destroy_membership
192 @user = User.find(params[:id])
177 @user = User.find(params[:id])
193 @membership = Member.find(params[:membership_id])
178 @membership = Member.find(params[:membership_id])
194 if request.post? && @membership.deletable?
179 if request.post? && @membership.deletable?
195 @membership.destroy
180 @membership.destroy
196 end
181 end
197 respond_to do |format|
182 respond_to do |format|
198 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
183 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
199 format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
184 format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
200 end
185 end
201 end
186 end
202 end
187 end
@@ -1,460 +1,471
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require "digest/sha1"
18 require "digest/sha1"
19
19
20 class User < Principal
20 class User < Principal
21
21
22 # Account statuses
22 # Account statuses
23 STATUS_ANONYMOUS = 0
23 STATUS_ANONYMOUS = 0
24 STATUS_ACTIVE = 1
24 STATUS_ACTIVE = 1
25 STATUS_REGISTERED = 2
25 STATUS_REGISTERED = 2
26 STATUS_LOCKED = 3
26 STATUS_LOCKED = 3
27
27
28 USER_FORMATS = {
28 USER_FORMATS = {
29 :firstname_lastname => '#{firstname} #{lastname}',
29 :firstname_lastname => '#{firstname} #{lastname}',
30 :firstname => '#{firstname}',
30 :firstname => '#{firstname}',
31 :lastname_firstname => '#{lastname} #{firstname}',
31 :lastname_firstname => '#{lastname} #{firstname}',
32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
33 :username => '#{login}'
33 :username => '#{login}'
34 }
34 }
35
35
36 MAIL_NOTIFICATION_OPTIONS = [
36 MAIL_NOTIFICATION_OPTIONS = [
37 [:all, :label_user_mail_option_all],
37 [:all, :label_user_mail_option_all],
38 [:selected, :label_user_mail_option_selected],
38 [:selected, :label_user_mail_option_selected],
39 [:none, :label_user_mail_option_none],
39 [:none, :label_user_mail_option_none],
40 [:only_my_events, :label_user_mail_option_only_my_events],
40 [:only_my_events, :label_user_mail_option_only_my_events],
41 [:only_assigned, :label_user_mail_option_only_assigned],
41 [:only_assigned, :label_user_mail_option_only_assigned],
42 [:only_owner, :label_user_mail_option_only_owner]
42 [:only_owner, :label_user_mail_option_only_owner]
43 ]
43 ]
44
44
45 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
45 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
46 :after_remove => Proc.new {|user, group| group.user_removed(user)}
46 :after_remove => Proc.new {|user, group| group.user_removed(user)}
47 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
47 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
48 has_many :changesets, :dependent => :nullify
48 has_many :changesets, :dependent => :nullify
49 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
49 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
50 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
50 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
51 has_one :api_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='api'"
51 has_one :api_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='api'"
52 belongs_to :auth_source
52 belongs_to :auth_source
53
53
54 # Active non-anonymous users scope
54 # Active non-anonymous users scope
55 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
55 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
56
56
57 acts_as_customizable
57 acts_as_customizable
58
58
59 attr_accessor :password, :password_confirmation
59 attr_accessor :password, :password_confirmation
60 attr_accessor :last_before_login_on
60 attr_accessor :last_before_login_on
61 # Prevents unauthorized assignments
61 # Prevents unauthorized assignments
62 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids
62 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids
63
63
64 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
64 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
65 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
65 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }, :case_sensitive => false
66 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
66 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
67 # Login must contain lettres, numbers, underscores only
67 # Login must contain lettres, numbers, underscores only
68 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
68 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
69 validates_length_of :login, :maximum => 30
69 validates_length_of :login, :maximum => 30
70 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
70 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
71 validates_length_of :firstname, :lastname, :maximum => 30
71 validates_length_of :firstname, :lastname, :maximum => 30
72 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
72 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
73 validates_length_of :mail, :maximum => 60, :allow_nil => true
73 validates_length_of :mail, :maximum => 60, :allow_nil => true
74 validates_confirmation_of :password, :allow_nil => true
74 validates_confirmation_of :password, :allow_nil => true
75
75
76 def before_create
76 def before_create
77 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
77 self.mail_notification = Setting.default_notification_option if self.mail_notification.blank?
78 true
78 true
79 end
79 end
80
80
81 def before_save
81 def before_save
82 # update hashed_password if password was set
82 # update hashed_password if password was set
83 self.hashed_password = User.hash_password(self.password) if self.password && self.auth_source_id.blank?
83 self.hashed_password = User.hash_password(self.password) if self.password && self.auth_source_id.blank?
84 end
84 end
85
85
86 def reload(*args)
86 def reload(*args)
87 @name = nil
87 @name = nil
88 super
88 super
89 end
89 end
90
90
91 def mail=(arg)
91 def mail=(arg)
92 write_attribute(:mail, arg.to_s.strip)
92 write_attribute(:mail, arg.to_s.strip)
93 end
93 end
94
94
95 def identity_url=(url)
95 def identity_url=(url)
96 if url.blank?
96 if url.blank?
97 write_attribute(:identity_url, '')
97 write_attribute(:identity_url, '')
98 else
98 else
99 begin
99 begin
100 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
100 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
101 rescue OpenIdAuthentication::InvalidOpenId
101 rescue OpenIdAuthentication::InvalidOpenId
102 # Invlaid url, don't save
102 # Invlaid url, don't save
103 end
103 end
104 end
104 end
105 self.read_attribute(:identity_url)
105 self.read_attribute(:identity_url)
106 end
106 end
107
107
108 # Returns the user that matches provided login and password, or nil
108 # Returns the user that matches provided login and password, or nil
109 def self.try_to_login(login, password)
109 def self.try_to_login(login, password)
110 # Make sure no one can sign in with an empty password
110 # Make sure no one can sign in with an empty password
111 return nil if password.to_s.empty?
111 return nil if password.to_s.empty?
112 user = find_by_login(login)
112 user = find_by_login(login)
113 if user
113 if user
114 # user is already in local database
114 # user is already in local database
115 return nil if !user.active?
115 return nil if !user.active?
116 if user.auth_source
116 if user.auth_source
117 # user has an external authentication method
117 # user has an external authentication method
118 return nil unless user.auth_source.authenticate(login, password)
118 return nil unless user.auth_source.authenticate(login, password)
119 else
119 else
120 # authentication with local password
120 # authentication with local password
121 return nil unless User.hash_password(password) == user.hashed_password
121 return nil unless User.hash_password(password) == user.hashed_password
122 end
122 end
123 else
123 else
124 # user is not yet registered, try to authenticate with available sources
124 # user is not yet registered, try to authenticate with available sources
125 attrs = AuthSource.authenticate(login, password)
125 attrs = AuthSource.authenticate(login, password)
126 if attrs
126 if attrs
127 user = new(attrs)
127 user = new(attrs)
128 user.login = login
128 user.login = login
129 user.language = Setting.default_language
129 user.language = Setting.default_language
130 if user.save
130 if user.save
131 user.reload
131 user.reload
132 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
132 logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source
133 end
133 end
134 end
134 end
135 end
135 end
136 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
136 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
137 user
137 user
138 rescue => text
138 rescue => text
139 raise text
139 raise text
140 end
140 end
141
141
142 # Returns the user who matches the given autologin +key+ or nil
142 # Returns the user who matches the given autologin +key+ or nil
143 def self.try_to_autologin(key)
143 def self.try_to_autologin(key)
144 tokens = Token.find_all_by_action_and_value('autologin', key)
144 tokens = Token.find_all_by_action_and_value('autologin', key)
145 # Make sure there's only 1 token that matches the key
145 # Make sure there's only 1 token that matches the key
146 if tokens.size == 1
146 if tokens.size == 1
147 token = tokens.first
147 token = tokens.first
148 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
148 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
149 token.user.update_attribute(:last_login_on, Time.now)
149 token.user.update_attribute(:last_login_on, Time.now)
150 token.user
150 token.user
151 end
151 end
152 end
152 end
153 end
153 end
154
154
155 # Return user's full name for display
155 # Return user's full name for display
156 def name(formatter = nil)
156 def name(formatter = nil)
157 if formatter
157 if formatter
158 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
158 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
159 else
159 else
160 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
160 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
161 end
161 end
162 end
162 end
163
163
164 def active?
164 def active?
165 self.status == STATUS_ACTIVE
165 self.status == STATUS_ACTIVE
166 end
166 end
167
167
168 def registered?
168 def registered?
169 self.status == STATUS_REGISTERED
169 self.status == STATUS_REGISTERED
170 end
170 end
171
171
172 def locked?
172 def locked?
173 self.status == STATUS_LOCKED
173 self.status == STATUS_LOCKED
174 end
174 end
175
175
176 def activate
176 def activate
177 self.status = STATUS_ACTIVE
177 self.status = STATUS_ACTIVE
178 end
178 end
179
179
180 def register
180 def register
181 self.status = STATUS_REGISTERED
181 self.status = STATUS_REGISTERED
182 end
182 end
183
183
184 def lock
184 def lock
185 self.status = STATUS_LOCKED
185 self.status = STATUS_LOCKED
186 end
186 end
187
187
188 def activate!
188 def activate!
189 update_attribute(:status, STATUS_ACTIVE)
189 update_attribute(:status, STATUS_ACTIVE)
190 end
190 end
191
191
192 def register!
192 def register!
193 update_attribute(:status, STATUS_REGISTERED)
193 update_attribute(:status, STATUS_REGISTERED)
194 end
194 end
195
195
196 def lock!
196 def lock!
197 update_attribute(:status, STATUS_LOCKED)
197 update_attribute(:status, STATUS_LOCKED)
198 end
198 end
199
199
200 def check_password?(clear_password)
200 def check_password?(clear_password)
201 if auth_source_id.present?
201 if auth_source_id.present?
202 auth_source.authenticate(self.login, clear_password)
202 auth_source.authenticate(self.login, clear_password)
203 else
203 else
204 User.hash_password(clear_password) == self.hashed_password
204 User.hash_password(clear_password) == self.hashed_password
205 end
205 end
206 end
206 end
207
207
208 # Does the backend storage allow this user to change their password?
208 # Does the backend storage allow this user to change their password?
209 def change_password_allowed?
209 def change_password_allowed?
210 return true if auth_source_id.blank?
210 return true if auth_source_id.blank?
211 return auth_source.allow_password_changes?
211 return auth_source.allow_password_changes?
212 end
212 end
213
213
214 # Generate and set a random password. Useful for automated user creation
214 # Generate and set a random password. Useful for automated user creation
215 # Based on Token#generate_token_value
215 # Based on Token#generate_token_value
216 #
216 #
217 def random_password
217 def random_password
218 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
218 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
219 password = ''
219 password = ''
220 40.times { |i| password << chars[rand(chars.size-1)] }
220 40.times { |i| password << chars[rand(chars.size-1)] }
221 self.password = password
221 self.password = password
222 self.password_confirmation = password
222 self.password_confirmation = password
223 self
223 self
224 end
224 end
225
225
226 def pref
226 def pref
227 self.preference ||= UserPreference.new(:user => self)
227 self.preference ||= UserPreference.new(:user => self)
228 end
228 end
229
229
230 def time_zone
230 def time_zone
231 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
231 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
232 end
232 end
233
233
234 def wants_comments_in_reverse_order?
234 def wants_comments_in_reverse_order?
235 self.pref[:comments_sorting] == 'desc'
235 self.pref[:comments_sorting] == 'desc'
236 end
236 end
237
237
238 # Return user's RSS key (a 40 chars long string), used to access feeds
238 # Return user's RSS key (a 40 chars long string), used to access feeds
239 def rss_key
239 def rss_key
240 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
240 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
241 token.value
241 token.value
242 end
242 end
243
243
244 # Return user's API key (a 40 chars long string), used to access the API
244 # Return user's API key (a 40 chars long string), used to access the API
245 def api_key
245 def api_key
246 token = self.api_token || self.create_api_token(:action => 'api')
246 token = self.api_token || self.create_api_token(:action => 'api')
247 token.value
247 token.value
248 end
248 end
249
249
250 # Return an array of project ids for which the user has explicitly turned mail notifications on
250 # Return an array of project ids for which the user has explicitly turned mail notifications on
251 def notified_projects_ids
251 def notified_projects_ids
252 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
252 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
253 end
253 end
254
254
255 def notified_project_ids=(ids)
255 def notified_project_ids=(ids)
256 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
256 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
257 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
257 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
258 @notified_projects_ids = nil
258 @notified_projects_ids = nil
259 notified_projects_ids
259 notified_projects_ids
260 end
260 end
261
261
262 # Only users that belong to more than 1 project can select projects for which they are notified
263 def valid_notification_options
264 # Note that @user.membership.size would fail since AR ignores
265 # :include association option when doing a count
266 if memberships.length < 1
267 MAIL_NOTIFICATION_OPTIONS.delete_if {|option| option.first == :selected}
268 else
269 MAIL_NOTIFICATION_OPTIONS
270 end
271 end
272
262 # Find a user account by matching the exact login and then a case-insensitive
273 # Find a user account by matching the exact login and then a case-insensitive
263 # version. Exact matches will be given priority.
274 # version. Exact matches will be given priority.
264 def self.find_by_login(login)
275 def self.find_by_login(login)
265 # force string comparison to be case sensitive on MySQL
276 # force string comparison to be case sensitive on MySQL
266 type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
277 type_cast = (ActiveRecord::Base.connection.adapter_name == 'MySQL') ? 'BINARY' : ''
267
278
268 # First look for an exact match
279 # First look for an exact match
269 user = first(:conditions => ["#{type_cast} login = ?", login])
280 user = first(:conditions => ["#{type_cast} login = ?", login])
270 # Fail over to case-insensitive if none was found
281 # Fail over to case-insensitive if none was found
271 user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase])
282 user ||= first(:conditions => ["#{type_cast} LOWER(login) = ?", login.to_s.downcase])
272 end
283 end
273
284
274 def self.find_by_rss_key(key)
285 def self.find_by_rss_key(key)
275 token = Token.find_by_value(key)
286 token = Token.find_by_value(key)
276 token && token.user.active? ? token.user : nil
287 token && token.user.active? ? token.user : nil
277 end
288 end
278
289
279 def self.find_by_api_key(key)
290 def self.find_by_api_key(key)
280 token = Token.find_by_action_and_value('api', key)
291 token = Token.find_by_action_and_value('api', key)
281 token && token.user.active? ? token.user : nil
292 token && token.user.active? ? token.user : nil
282 end
293 end
283
294
284 # Makes find_by_mail case-insensitive
295 # Makes find_by_mail case-insensitive
285 def self.find_by_mail(mail)
296 def self.find_by_mail(mail)
286 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
297 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
287 end
298 end
288
299
289 def to_s
300 def to_s
290 name
301 name
291 end
302 end
292
303
293 # Returns the current day according to user's time zone
304 # Returns the current day according to user's time zone
294 def today
305 def today
295 if time_zone.nil?
306 if time_zone.nil?
296 Date.today
307 Date.today
297 else
308 else
298 Time.now.in_time_zone(time_zone).to_date
309 Time.now.in_time_zone(time_zone).to_date
299 end
310 end
300 end
311 end
301
312
302 def logged?
313 def logged?
303 true
314 true
304 end
315 end
305
316
306 def anonymous?
317 def anonymous?
307 !logged?
318 !logged?
308 end
319 end
309
320
310 # Return user's roles for project
321 # Return user's roles for project
311 def roles_for_project(project)
322 def roles_for_project(project)
312 roles = []
323 roles = []
313 # No role on archived projects
324 # No role on archived projects
314 return roles unless project && project.active?
325 return roles unless project && project.active?
315 if logged?
326 if logged?
316 # Find project membership
327 # Find project membership
317 membership = memberships.detect {|m| m.project_id == project.id}
328 membership = memberships.detect {|m| m.project_id == project.id}
318 if membership
329 if membership
319 roles = membership.roles
330 roles = membership.roles
320 else
331 else
321 @role_non_member ||= Role.non_member
332 @role_non_member ||= Role.non_member
322 roles << @role_non_member
333 roles << @role_non_member
323 end
334 end
324 else
335 else
325 @role_anonymous ||= Role.anonymous
336 @role_anonymous ||= Role.anonymous
326 roles << @role_anonymous
337 roles << @role_anonymous
327 end
338 end
328 roles
339 roles
329 end
340 end
330
341
331 # Return true if the user is a member of project
342 # Return true if the user is a member of project
332 def member_of?(project)
343 def member_of?(project)
333 !roles_for_project(project).detect {|role| role.member?}.nil?
344 !roles_for_project(project).detect {|role| role.member?}.nil?
334 end
345 end
335
346
336 # Return true if the user is allowed to do the specified action on project
347 # Return true if the user is allowed to do the specified action on project
337 # action can be:
348 # action can be:
338 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
349 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
339 # * a permission Symbol (eg. :edit_project)
350 # * a permission Symbol (eg. :edit_project)
340 def allowed_to?(action, project, options={})
351 def allowed_to?(action, project, options={})
341 if project
352 if project
342 # No action allowed on archived projects
353 # No action allowed on archived projects
343 return false unless project.active?
354 return false unless project.active?
344 # No action allowed on disabled modules
355 # No action allowed on disabled modules
345 return false unless project.allows_to?(action)
356 return false unless project.allows_to?(action)
346 # Admin users are authorized for anything else
357 # Admin users are authorized for anything else
347 return true if admin?
358 return true if admin?
348
359
349 roles = roles_for_project(project)
360 roles = roles_for_project(project)
350 return false unless roles
361 return false unless roles
351 roles.detect {|role| (project.is_public? || role.member?) && role.allowed_to?(action)}
362 roles.detect {|role| (project.is_public? || role.member?) && role.allowed_to?(action)}
352
363
353 elsif options[:global]
364 elsif options[:global]
354 # Admin users are always authorized
365 # Admin users are always authorized
355 return true if admin?
366 return true if admin?
356
367
357 # authorize if user has at least one role that has this permission
368 # authorize if user has at least one role that has this permission
358 roles = memberships.collect {|m| m.roles}.flatten.uniq
369 roles = memberships.collect {|m| m.roles}.flatten.uniq
359 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
370 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
360 else
371 else
361 false
372 false
362 end
373 end
363 end
374 end
364
375
365 # Is the user allowed to do the specified action on any project?
376 # Is the user allowed to do the specified action on any project?
366 # See allowed_to? for the actions and valid options.
377 # See allowed_to? for the actions and valid options.
367 def allowed_to_globally?(action, options)
378 def allowed_to_globally?(action, options)
368 allowed_to?(action, nil, options.reverse_merge(:global => true))
379 allowed_to?(action, nil, options.reverse_merge(:global => true))
369 end
380 end
370
381
371 # Utility method to help check if a user should be notified about an
382 # Utility method to help check if a user should be notified about an
372 # event.
383 # event.
373 #
384 #
374 # TODO: only supports Issue events currently
385 # TODO: only supports Issue events currently
375 def notify_about?(object)
386 def notify_about?(object)
376 case mail_notification.to_sym
387 case mail_notification.to_sym
377 when :all
388 when :all
378 true
389 true
379 when :selected
390 when :selected
380 # Handled by the Project
391 # Handled by the Project
381 when :none
392 when :none
382 false
393 false
383 when :only_my_events
394 when :only_my_events
384 if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
395 if object.is_a?(Issue) && (object.author == self || object.assigned_to == self)
385 true
396 true
386 else
397 else
387 false
398 false
388 end
399 end
389 when :only_assigned
400 when :only_assigned
390 if object.is_a?(Issue) && object.assigned_to == self
401 if object.is_a?(Issue) && object.assigned_to == self
391 true
402 true
392 else
403 else
393 false
404 false
394 end
405 end
395 when :only_owner
406 when :only_owner
396 if object.is_a?(Issue) && object.author == self
407 if object.is_a?(Issue) && object.author == self
397 true
408 true
398 else
409 else
399 false
410 false
400 end
411 end
401 else
412 else
402 false
413 false
403 end
414 end
404 end
415 end
405
416
406 def self.current=(user)
417 def self.current=(user)
407 @current_user = user
418 @current_user = user
408 end
419 end
409
420
410 def self.current
421 def self.current
411 @current_user ||= User.anonymous
422 @current_user ||= User.anonymous
412 end
423 end
413
424
414 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
425 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
415 # one anonymous user per database.
426 # one anonymous user per database.
416 def self.anonymous
427 def self.anonymous
417 anonymous_user = AnonymousUser.find(:first)
428 anonymous_user = AnonymousUser.find(:first)
418 if anonymous_user.nil?
429 if anonymous_user.nil?
419 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
430 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
420 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
431 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
421 end
432 end
422 anonymous_user
433 anonymous_user
423 end
434 end
424
435
425 protected
436 protected
426
437
427 def validate
438 def validate
428 # Password length validation based on setting
439 # Password length validation based on setting
429 if !password.nil? && password.size < Setting.password_min_length.to_i
440 if !password.nil? && password.size < Setting.password_min_length.to_i
430 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
441 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
431 end
442 end
432 end
443 end
433
444
434 private
445 private
435
446
436 # Return password digest
447 # Return password digest
437 def self.hash_password(clear_password)
448 def self.hash_password(clear_password)
438 Digest::SHA1.hexdigest(clear_password || "")
449 Digest::SHA1.hexdigest(clear_password || "")
439 end
450 end
440 end
451 end
441
452
442 class AnonymousUser < User
453 class AnonymousUser < User
443
454
444 def validate_on_create
455 def validate_on_create
445 # There should be only one AnonymousUser in the database
456 # There should be only one AnonymousUser in the database
446 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
457 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
447 end
458 end
448
459
449 def available_custom_fields
460 def available_custom_fields
450 []
461 []
451 end
462 end
452
463
453 # Overrides a few properties
464 # Overrides a few properties
454 def logged?; false end
465 def logged?; false end
455 def admin; false end
466 def admin; false end
456 def name(*args); I18n.t(:label_user_anonymous) end
467 def name(*args); I18n.t(:label_user_anonymous) end
457 def mail; nil end
468 def mail; nil end
458 def time_zone; nil end
469 def time_zone; nil end
459 def rss_key; nil end
470 def rss_key; nil end
460 end
471 end
General Comments 0
You need to be logged in to leave comments. Login now