##// END OF EJS Templates
Fixed: Pre-filled time tracking date ignores timezone (#4160)....
Jean-Philippe Lang -
r2898:6245f49934d2
parent child
Show More
@@ -1,308 +1,308
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class TimelogController < ApplicationController
19 19 menu_item :issues
20 20 before_filter :find_project, :authorize, :only => [:edit, :destroy]
21 21 before_filter :find_optional_project, :only => [:report, :details]
22 22
23 23 verify :method => :post, :only => :destroy, :redirect_to => { :action => :details }
24 24
25 25 helper :sort
26 26 include SortHelper
27 27 helper :issues
28 28 include TimelogHelper
29 29 helper :custom_fields
30 30 include CustomFieldsHelper
31 31
32 32 def report
33 33 @available_criterias = { 'project' => {:sql => "#{TimeEntry.table_name}.project_id",
34 34 :klass => Project,
35 35 :label => :label_project},
36 36 'version' => {:sql => "#{Issue.table_name}.fixed_version_id",
37 37 :klass => Version,
38 38 :label => :label_version},
39 39 'category' => {:sql => "#{Issue.table_name}.category_id",
40 40 :klass => IssueCategory,
41 41 :label => :field_category},
42 42 'member' => {:sql => "#{TimeEntry.table_name}.user_id",
43 43 :klass => User,
44 44 :label => :label_member},
45 45 'tracker' => {:sql => "#{Issue.table_name}.tracker_id",
46 46 :klass => Tracker,
47 47 :label => :label_tracker},
48 48 'activity' => {:sql => "#{TimeEntry.table_name}.activity_id",
49 49 :klass => TimeEntryActivity,
50 50 :label => :label_activity},
51 51 'issue' => {:sql => "#{TimeEntry.table_name}.issue_id",
52 52 :klass => Issue,
53 53 :label => :label_issue}
54 54 }
55 55
56 56 # Add list and boolean custom fields as available criterias
57 57 custom_fields = (@project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields)
58 58 custom_fields.select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
59 59 @available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Issue' AND c.customized_id = #{Issue.table_name}.id)",
60 60 :format => cf.field_format,
61 61 :label => cf.name}
62 62 end if @project
63 63
64 64 # Add list and boolean time entry custom fields
65 65 TimeEntryCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
66 66 @available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'TimeEntry' AND c.customized_id = #{TimeEntry.table_name}.id)",
67 67 :format => cf.field_format,
68 68 :label => cf.name}
69 69 end
70 70
71 71 # Add list and boolean time entry activity custom fields
72 72 TimeEntryActivityCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf|
73 73 @available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Enumeration' AND c.customized_id = #{TimeEntry.table_name}.activity_id)",
74 74 :format => cf.field_format,
75 75 :label => cf.name}
76 76 end
77 77
78 78 @criterias = params[:criterias] || []
79 79 @criterias = @criterias.select{|criteria| @available_criterias.has_key? criteria}
80 80 @criterias.uniq!
81 81 @criterias = @criterias[0,3]
82 82
83 83 @columns = (params[:columns] && %w(year month week day).include?(params[:columns])) ? params[:columns] : 'month'
84 84
85 85 retrieve_date_range
86 86
87 87 unless @criterias.empty?
88 88 sql_select = @criterias.collect{|criteria| @available_criterias[criteria][:sql] + " AS " + criteria}.join(', ')
89 89 sql_group_by = @criterias.collect{|criteria| @available_criterias[criteria][:sql]}.join(', ')
90 90 sql_condition = ''
91 91
92 92 if @project.nil?
93 93 sql_condition = Project.allowed_to_condition(User.current, :view_time_entries)
94 94 elsif @issue.nil?
95 95 sql_condition = @project.project_condition(Setting.display_subprojects_issues?)
96 96 else
97 97 sql_condition = "#{TimeEntry.table_name}.issue_id = #{@issue.id}"
98 98 end
99 99
100 100 sql = "SELECT #{sql_select}, tyear, tmonth, tweek, spent_on, SUM(hours) AS hours"
101 101 sql << " FROM #{TimeEntry.table_name}"
102 102 sql << " LEFT JOIN #{Issue.table_name} ON #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id"
103 103 sql << " LEFT JOIN #{Project.table_name} ON #{TimeEntry.table_name}.project_id = #{Project.table_name}.id"
104 104 sql << " WHERE"
105 105 sql << " (%s) AND" % sql_condition
106 106 sql << " (spent_on BETWEEN '%s' AND '%s')" % [ActiveRecord::Base.connection.quoted_date(@from), ActiveRecord::Base.connection.quoted_date(@to)]
107 107 sql << " GROUP BY #{sql_group_by}, tyear, tmonth, tweek, spent_on"
108 108
109 109 @hours = ActiveRecord::Base.connection.select_all(sql)
110 110
111 111 @hours.each do |row|
112 112 case @columns
113 113 when 'year'
114 114 row['year'] = row['tyear']
115 115 when 'month'
116 116 row['month'] = "#{row['tyear']}-#{row['tmonth']}"
117 117 when 'week'
118 118 row['week'] = "#{row['tyear']}-#{row['tweek']}"
119 119 when 'day'
120 120 row['day'] = "#{row['spent_on']}"
121 121 end
122 122 end
123 123
124 124 @total_hours = @hours.inject(0) {|s,k| s = s + k['hours'].to_f}
125 125
126 126 @periods = []
127 127 # Date#at_beginning_of_ not supported in Rails 1.2.x
128 128 date_from = @from.to_time
129 129 # 100 columns max
130 130 while date_from <= @to.to_time && @periods.length < 100
131 131 case @columns
132 132 when 'year'
133 133 @periods << "#{date_from.year}"
134 134 date_from = (date_from + 1.year).at_beginning_of_year
135 135 when 'month'
136 136 @periods << "#{date_from.year}-#{date_from.month}"
137 137 date_from = (date_from + 1.month).at_beginning_of_month
138 138 when 'week'
139 139 @periods << "#{date_from.year}-#{date_from.to_date.cweek}"
140 140 date_from = (date_from + 7.day).at_beginning_of_week
141 141 when 'day'
142 142 @periods << "#{date_from.to_date}"
143 143 date_from = date_from + 1.day
144 144 end
145 145 end
146 146 end
147 147
148 148 respond_to do |format|
149 149 format.html { render :layout => !request.xhr? }
150 150 format.csv { send_data(report_to_csv(@criterias, @periods, @hours), :type => 'text/csv; header=present', :filename => 'timelog.csv') }
151 151 end
152 152 end
153 153
154 154 def details
155 155 sort_init 'spent_on', 'desc'
156 156 sort_update 'spent_on' => 'spent_on',
157 157 'user' => 'user_id',
158 158 'activity' => 'activity_id',
159 159 'project' => "#{Project.table_name}.name",
160 160 'issue' => 'issue_id',
161 161 'hours' => 'hours'
162 162
163 163 cond = ARCondition.new
164 164 if @project.nil?
165 165 cond << Project.allowed_to_condition(User.current, :view_time_entries)
166 166 elsif @issue.nil?
167 167 cond << @project.project_condition(Setting.display_subprojects_issues?)
168 168 else
169 169 cond << ["#{TimeEntry.table_name}.issue_id = ?", @issue.id]
170 170 end
171 171
172 172 retrieve_date_range
173 173 cond << ['spent_on BETWEEN ? AND ?', @from, @to]
174 174
175 175 TimeEntry.visible_by(User.current) do
176 176 respond_to do |format|
177 177 format.html {
178 178 # Paginate results
179 179 @entry_count = TimeEntry.count(:include => :project, :conditions => cond.conditions)
180 180 @entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
181 181 @entries = TimeEntry.find(:all,
182 182 :include => [:project, :activity, :user, {:issue => :tracker}],
183 183 :conditions => cond.conditions,
184 184 :order => sort_clause,
185 185 :limit => @entry_pages.items_per_page,
186 186 :offset => @entry_pages.current.offset)
187 187 @total_hours = TimeEntry.sum(:hours, :include => :project, :conditions => cond.conditions).to_f
188 188
189 189 render :layout => !request.xhr?
190 190 }
191 191 format.atom {
192 192 entries = TimeEntry.find(:all,
193 193 :include => [:project, :activity, :user, {:issue => :tracker}],
194 194 :conditions => cond.conditions,
195 195 :order => "#{TimeEntry.table_name}.created_on DESC",
196 196 :limit => Setting.feeds_limit.to_i)
197 197 render_feed(entries, :title => l(:label_spent_time))
198 198 }
199 199 format.csv {
200 200 # Export all entries
201 201 @entries = TimeEntry.find(:all,
202 202 :include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
203 203 :conditions => cond.conditions,
204 204 :order => sort_clause)
205 205 send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
206 206 }
207 207 end
208 208 end
209 209 end
210 210
211 211 def edit
212 212 render_403 and return if @time_entry && !@time_entry.editable_by?(User.current)
213 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
213 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
214 214 @time_entry.attributes = params[:time_entry]
215 215
216 216 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
217 217
218 218 if request.post? and @time_entry.save
219 219 flash[:notice] = l(:notice_successful_update)
220 220 redirect_back_or_default :action => 'details', :project_id => @time_entry.project
221 221 return
222 222 end
223 223 end
224 224
225 225 def destroy
226 226 render_404 and return unless @time_entry
227 227 render_403 and return unless @time_entry.editable_by?(User.current)
228 228 @time_entry.destroy
229 229 flash[:notice] = l(:notice_successful_delete)
230 230 redirect_to :back
231 231 rescue ::ActionController::RedirectBackError
232 232 redirect_to :action => 'details', :project_id => @time_entry.project
233 233 end
234 234
235 235 private
236 236 def find_project
237 237 if params[:id]
238 238 @time_entry = TimeEntry.find(params[:id])
239 239 @project = @time_entry.project
240 240 elsif params[:issue_id]
241 241 @issue = Issue.find(params[:issue_id])
242 242 @project = @issue.project
243 243 elsif params[:project_id]
244 244 @project = Project.find(params[:project_id])
245 245 else
246 246 render_404
247 247 return false
248 248 end
249 249 rescue ActiveRecord::RecordNotFound
250 250 render_404
251 251 end
252 252
253 253 def find_optional_project
254 254 if !params[:issue_id].blank?
255 255 @issue = Issue.find(params[:issue_id])
256 256 @project = @issue.project
257 257 elsif !params[:project_id].blank?
258 258 @project = Project.find(params[:project_id])
259 259 end
260 260 deny_access unless User.current.allowed_to?(:view_time_entries, @project, :global => true)
261 261 end
262 262
263 263 # Retrieves the date range based on predefined ranges or specific from/to param dates
264 264 def retrieve_date_range
265 265 @free_period = false
266 266 @from, @to = nil, nil
267 267
268 268 if params[:period_type] == '1' || (params[:period_type].nil? && !params[:period].nil?)
269 269 case params[:period].to_s
270 270 when 'today'
271 271 @from = @to = Date.today
272 272 when 'yesterday'
273 273 @from = @to = Date.today - 1
274 274 when 'current_week'
275 275 @from = Date.today - (Date.today.cwday - 1)%7
276 276 @to = @from + 6
277 277 when 'last_week'
278 278 @from = Date.today - 7 - (Date.today.cwday - 1)%7
279 279 @to = @from + 6
280 280 when '7_days'
281 281 @from = Date.today - 7
282 282 @to = Date.today
283 283 when 'current_month'
284 284 @from = Date.civil(Date.today.year, Date.today.month, 1)
285 285 @to = (@from >> 1) - 1
286 286 when 'last_month'
287 287 @from = Date.civil(Date.today.year, Date.today.month, 1) << 1
288 288 @to = (@from >> 1) - 1
289 289 when '30_days'
290 290 @from = Date.today - 30
291 291 @to = Date.today
292 292 when 'current_year'
293 293 @from = Date.civil(Date.today.year, 1, 1)
294 294 @to = Date.civil(Date.today.year, 12, 31)
295 295 end
296 296 elsif params[:period_type] == '2' || (params[:period_type].nil? && (!params[:from].nil? || !params[:to].nil?))
297 297 begin; @from = params[:from].to_s.to_date unless params[:from].blank?; rescue; end
298 298 begin; @to = params[:to].to_s.to_date unless params[:to].blank?; rescue; end
299 299 @free_period = true
300 300 else
301 301 # default
302 302 end
303 303
304 304 @from, @to = @to, @from if @from && @to && @from > @to
305 305 @from ||= (TimeEntry.minimum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) || Date.today) - 1
306 306 @to ||= (TimeEntry.maximum(:spent_on, :include => :project, :conditions => Project.allowed_to_condition(User.current, :view_time_entries)) || Date.today)
307 307 end
308 308 end
@@ -1,344 +1,353
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require "digest/sha1"
19 19
20 20 class User < Principal
21 21
22 22 # Account statuses
23 23 STATUS_ANONYMOUS = 0
24 24 STATUS_ACTIVE = 1
25 25 STATUS_REGISTERED = 2
26 26 STATUS_LOCKED = 3
27 27
28 28 USER_FORMATS = {
29 29 :firstname_lastname => '#{firstname} #{lastname}',
30 30 :firstname => '#{firstname}',
31 31 :lastname_firstname => '#{lastname} #{firstname}',
32 32 :lastname_coma_firstname => '#{lastname}, #{firstname}',
33 33 :username => '#{login}'
34 34 }
35 35
36 36 has_and_belongs_to_many :groups, :after_add => Proc.new {|user, group| group.user_added(user)},
37 37 :after_remove => Proc.new {|user, group| group.user_removed(user)}
38 38 has_many :issue_categories, :foreign_key => 'assigned_to_id', :dependent => :nullify
39 39 has_many :changesets, :dependent => :nullify
40 40 has_one :preference, :dependent => :destroy, :class_name => 'UserPreference'
41 41 has_one :rss_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='feeds'"
42 42 belongs_to :auth_source
43 43
44 44 # Active non-anonymous users scope
45 45 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
46 46
47 47 acts_as_customizable
48 48
49 49 attr_accessor :password, :password_confirmation
50 50 attr_accessor :last_before_login_on
51 51 # Prevents unauthorized assignments
52 52 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids
53 53
54 54 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
55 55 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
56 56 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
57 57 # Login must contain lettres, numbers, underscores only
58 58 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
59 59 validates_length_of :login, :maximum => 30
60 60 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
61 61 validates_length_of :firstname, :lastname, :maximum => 30
62 62 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
63 63 validates_length_of :mail, :maximum => 60, :allow_nil => true
64 64 validates_confirmation_of :password, :allow_nil => true
65 65
66 66 def before_create
67 67 self.mail_notification = false
68 68 true
69 69 end
70 70
71 71 def before_save
72 72 # update hashed_password if password was set
73 73 self.hashed_password = User.hash_password(self.password) if self.password
74 74 end
75 75
76 76 def reload(*args)
77 77 @name = nil
78 78 super
79 79 end
80 80
81 81 def identity_url=(url)
82 82 if url.blank?
83 83 write_attribute(:identity_url, '')
84 84 else
85 85 begin
86 86 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
87 87 rescue OpenIdAuthentication::InvalidOpenId
88 88 # Invlaid url, don't save
89 89 end
90 90 end
91 91 self.read_attribute(:identity_url)
92 92 end
93 93
94 94 # Returns the user that matches provided login and password, or nil
95 95 def self.try_to_login(login, password)
96 96 # Make sure no one can sign in with an empty password
97 97 return nil if password.to_s.empty?
98 98 user = find(:first, :conditions => ["login=?", login])
99 99 if user
100 100 # user is already in local database
101 101 return nil if !user.active?
102 102 if user.auth_source
103 103 # user has an external authentication method
104 104 return nil unless user.auth_source.authenticate(login, password)
105 105 else
106 106 # authentication with local password
107 107 return nil unless User.hash_password(password) == user.hashed_password
108 108 end
109 109 else
110 110 # user is not yet registered, try to authenticate with available sources
111 111 attrs = AuthSource.authenticate(login, password)
112 112 if attrs
113 113 user = new(*attrs)
114 114 user.login = login
115 115 user.language = Setting.default_language
116 116 if user.save
117 117 user.reload
118 118 logger.info("User '#{user.login}' created from the LDAP") if logger
119 119 end
120 120 end
121 121 end
122 122 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
123 123 user
124 124 rescue => text
125 125 raise text
126 126 end
127 127
128 128 # Returns the user who matches the given autologin +key+ or nil
129 129 def self.try_to_autologin(key)
130 130 tokens = Token.find_all_by_action_and_value('autologin', key)
131 131 # Make sure there's only 1 token that matches the key
132 132 if tokens.size == 1
133 133 token = tokens.first
134 134 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
135 135 token.user.update_attribute(:last_login_on, Time.now)
136 136 token.user
137 137 end
138 138 end
139 139 end
140 140
141 141 # Return user's full name for display
142 142 def name(formatter = nil)
143 143 if formatter
144 144 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
145 145 else
146 146 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
147 147 end
148 148 end
149 149
150 150 def active?
151 151 self.status == STATUS_ACTIVE
152 152 end
153 153
154 154 def registered?
155 155 self.status == STATUS_REGISTERED
156 156 end
157 157
158 158 def locked?
159 159 self.status == STATUS_LOCKED
160 160 end
161 161
162 162 def check_password?(clear_password)
163 163 User.hash_password(clear_password) == self.hashed_password
164 164 end
165 165
166 166 # Generate and set a random password. Useful for automated user creation
167 167 # Based on Token#generate_token_value
168 168 #
169 169 def random_password
170 170 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
171 171 password = ''
172 172 40.times { |i| password << chars[rand(chars.size-1)] }
173 173 self.password = password
174 174 self.password_confirmation = password
175 175 self
176 176 end
177 177
178 178 def pref
179 179 self.preference ||= UserPreference.new(:user => self)
180 180 end
181 181
182 182 def time_zone
183 183 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
184 184 end
185 185
186 186 def wants_comments_in_reverse_order?
187 187 self.pref[:comments_sorting] == 'desc'
188 188 end
189 189
190 190 # Return user's RSS key (a 40 chars long string), used to access feeds
191 191 def rss_key
192 192 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
193 193 token.value
194 194 end
195 195
196 196 # Return an array of project ids for which the user has explicitly turned mail notifications on
197 197 def notified_projects_ids
198 198 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
199 199 end
200 200
201 201 def notified_project_ids=(ids)
202 202 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
203 203 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
204 204 @notified_projects_ids = nil
205 205 notified_projects_ids
206 206 end
207 207
208 208 def self.find_by_rss_key(key)
209 209 token = Token.find_by_value(key)
210 210 token && token.user.active? ? token.user : nil
211 211 end
212 212
213 213 # Makes find_by_mail case-insensitive
214 214 def self.find_by_mail(mail)
215 215 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
216 216 end
217 217
218 218 # Sort users by their display names
219 219 def <=>(user)
220 220 self.to_s.downcase <=> user.to_s.downcase
221 221 end
222 222
223 223 def to_s
224 224 name
225 225 end
226 226
227 # Returns the current day according to user's time zone
228 def today
229 if time_zone.nil?
230 Date.today
231 else
232 Time.now.in_time_zone(time_zone).to_date
233 end
234 end
235
227 236 def logged?
228 237 true
229 238 end
230 239
231 240 def anonymous?
232 241 !logged?
233 242 end
234 243
235 244 # Return user's roles for project
236 245 def roles_for_project(project)
237 246 roles = []
238 247 # No role on archived projects
239 248 return roles unless project && project.active?
240 249 if logged?
241 250 # Find project membership
242 251 membership = memberships.detect {|m| m.project_id == project.id}
243 252 if membership
244 253 roles = membership.roles
245 254 else
246 255 @role_non_member ||= Role.non_member
247 256 roles << @role_non_member
248 257 end
249 258 else
250 259 @role_anonymous ||= Role.anonymous
251 260 roles << @role_anonymous
252 261 end
253 262 roles
254 263 end
255 264
256 265 # Return true if the user is a member of project
257 266 def member_of?(project)
258 267 !roles_for_project(project).detect {|role| role.member?}.nil?
259 268 end
260 269
261 270 # Return true if the user is allowed to do the specified action on project
262 271 # action can be:
263 272 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
264 273 # * a permission Symbol (eg. :edit_project)
265 274 def allowed_to?(action, project, options={})
266 275 if project
267 276 # No action allowed on archived projects
268 277 return false unless project.active?
269 278 # No action allowed on disabled modules
270 279 return false unless project.allows_to?(action)
271 280 # Admin users are authorized for anything else
272 281 return true if admin?
273 282
274 283 roles = roles_for_project(project)
275 284 return false unless roles
276 285 roles.detect {|role| (project.is_public? || role.member?) && role.allowed_to?(action)}
277 286
278 287 elsif options[:global]
279 288 # Admin users are always authorized
280 289 return true if admin?
281 290
282 291 # authorize if user has at least one role that has this permission
283 292 roles = memberships.collect {|m| m.roles}.flatten.uniq
284 293 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
285 294 else
286 295 false
287 296 end
288 297 end
289 298
290 299 def self.current=(user)
291 300 @current_user = user
292 301 end
293 302
294 303 def self.current
295 304 @current_user ||= User.anonymous
296 305 end
297 306
298 307 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
299 308 # one anonymous user per database.
300 309 def self.anonymous
301 310 anonymous_user = AnonymousUser.find(:first)
302 311 if anonymous_user.nil?
303 312 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
304 313 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
305 314 end
306 315 anonymous_user
307 316 end
308 317
309 318 protected
310 319
311 320 def validate
312 321 # Password length validation based on setting
313 322 if !password.nil? && password.size < Setting.password_min_length.to_i
314 323 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
315 324 end
316 325 end
317 326
318 327 private
319 328
320 329 # Return password digest
321 330 def self.hash_password(clear_password)
322 331 Digest::SHA1.hexdigest(clear_password || "")
323 332 end
324 333 end
325 334
326 335 class AnonymousUser < User
327 336
328 337 def validate_on_create
329 338 # There should be only one AnonymousUser in the database
330 339 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
331 340 end
332 341
333 342 def available_custom_fields
334 343 []
335 344 end
336 345
337 346 # Overrides a few properties
338 347 def logged?; false end
339 348 def admin; false end
340 349 def name; 'Anonymous' end
341 350 def mail; nil end
342 351 def time_zone; nil end
343 352 def rss_key; nil end
344 353 end
General Comments 0
You need to be logged in to leave comments. Login now