##// END OF EJS Templates
Added an API token for each User to use when making API requests. (#3920)...
Eric Davis -
r3103:aa9951b38b27
parent child
Show More
@@ -0,0 +1,13
1 class AddApiKeysForUsers < ActiveRecord::Migration
2 def self.up
3 say_with_time("Generating API keys for active users") do
4 User.active.all(:include => :api_token).each do |user|
5 user.api_key
6 end
7 end
8 end
9
10 def self.down
11 # No-op
12 end
13 end
@@ -1,170 +1,183
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 class MyController < ApplicationController
19 19 before_filter :require_login
20 20
21 21 helper :issues
22 22 helper :custom_fields
23 23
24 24 BLOCKS = { 'issuesassignedtome' => :label_assigned_to_me_issues,
25 25 'issuesreportedbyme' => :label_reported_issues,
26 26 'issueswatched' => :label_watched_issues,
27 27 'news' => :label_news_latest,
28 28 'calendar' => :label_calendar,
29 29 'documents' => :label_document_plural,
30 30 'timelog' => :label_spent_time
31 31 }.merge(Redmine::Views::MyPage::Block.additional_blocks).freeze
32 32
33 33 DEFAULT_LAYOUT = { 'left' => ['issuesassignedtome'],
34 34 'right' => ['issuesreportedbyme']
35 35 }.freeze
36 36
37 37 verify :xhr => true,
38 38 :only => [:add_block, :remove_block, :order_blocks]
39 39
40 40 def index
41 41 page
42 42 render :action => 'page'
43 43 end
44 44
45 45 # Show user's page
46 46 def page
47 47 @user = User.current
48 48 @blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT
49 49 end
50 50
51 51 # Edit user's account
52 52 def account
53 53 @user = User.current
54 54 @pref = @user.pref
55 55 if request.post?
56 56 @user.attributes = params[:user]
57 57 @user.mail_notification = (params[:notification_option] == 'all')
58 58 @user.pref.attributes = params[:pref]
59 59 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
60 60 if @user.save
61 61 @user.pref.save
62 62 @user.notified_project_ids = (params[:notification_option] == 'selected' ? params[:notified_project_ids] : [])
63 63 set_language_if_valid @user.language
64 64 flash[:notice] = l(:notice_account_updated)
65 65 redirect_to :action => 'account'
66 66 return
67 67 end
68 68 end
69 69 @notification_options = [[l(:label_user_mail_option_all), 'all'],
70 70 [l(:label_user_mail_option_none), 'none']]
71 71 # Only users that belong to more than 1 project can select projects for which they are notified
72 72 # Note that @user.membership.size would fail since AR ignores :include association option when doing a count
73 73 @notification_options.insert 1, [l(:label_user_mail_option_selected), 'selected'] if @user.memberships.length > 1
74 74 @notification_option = @user.mail_notification? ? 'all' : (@user.notified_projects_ids.empty? ? 'none' : 'selected')
75 75 end
76 76
77 77 # Manage user's password
78 78 def password
79 79 @user = User.current
80 80 if @user.auth_source_id
81 81 flash[:error] = l(:notice_can_t_change_password)
82 82 redirect_to :action => 'account'
83 83 return
84 84 end
85 85 if request.post?
86 86 if @user.check_password?(params[:password])
87 87 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
88 88 if @user.save
89 89 flash[:notice] = l(:notice_account_password_updated)
90 90 redirect_to :action => 'account'
91 91 end
92 92 else
93 93 flash[:error] = l(:notice_account_wrong_password)
94 94 end
95 95 end
96 96 end
97 97
98 98 # Create a new feeds key
99 99 def reset_rss_key
100 100 if request.post?
101 101 if User.current.rss_token
102 102 User.current.rss_token.destroy
103 103 User.current.reload
104 104 end
105 105 User.current.rss_key
106 106 flash[:notice] = l(:notice_feeds_access_key_reseted)
107 107 end
108 108 redirect_to :action => 'account'
109 109 end
110 110
111 # Create a new API key
112 def reset_api_key
113 if request.post?
114 if User.current.api_token
115 User.current.api_token.destroy
116 User.current.reload
117 end
118 User.current.api_key
119 flash[:notice] = l(:notice_api_access_key_reseted)
120 end
121 redirect_to :action => 'account'
122 end
123
111 124 # User's page layout configuration
112 125 def page_layout
113 126 @user = User.current
114 127 @blocks = @user.pref[:my_page_layout] || DEFAULT_LAYOUT.dup
115 128 @block_options = []
116 129 BLOCKS.each {|k, v| @block_options << [l("my.blocks.#{v}", :default => [v, v.to_s.humanize]), k.dasherize]}
117 130 end
118 131
119 132 # Add a block to user's page
120 133 # The block is added on top of the page
121 134 # params[:block] : id of the block to add
122 135 def add_block
123 136 block = params[:block].to_s.underscore
124 137 (render :nothing => true; return) unless block && (BLOCKS.keys.include? block)
125 138 @user = User.current
126 139 layout = @user.pref[:my_page_layout] || {}
127 140 # remove if already present in a group
128 141 %w(top left right).each {|f| (layout[f] ||= []).delete block }
129 142 # add it on top
130 143 layout['top'].unshift block
131 144 @user.pref[:my_page_layout] = layout
132 145 @user.pref.save
133 146 render :partial => "block", :locals => {:user => @user, :block_name => block}
134 147 end
135 148
136 149 # Remove a block to user's page
137 150 # params[:block] : id of the block to remove
138 151 def remove_block
139 152 block = params[:block].to_s.underscore
140 153 @user = User.current
141 154 # remove block in all groups
142 155 layout = @user.pref[:my_page_layout] || {}
143 156 %w(top left right).each {|f| (layout[f] ||= []).delete block }
144 157 @user.pref[:my_page_layout] = layout
145 158 @user.pref.save
146 159 render :nothing => true
147 160 end
148 161
149 162 # Change blocks order on user's page
150 163 # params[:group] : group to order (top, left or right)
151 164 # params[:list-(top|left|right)] : array of block ids of the group
152 165 def order_blocks
153 166 group = params[:group]
154 167 @user = User.current
155 168 if group.is_a?(String)
156 169 group_items = (params["list-#{group}"] || []).collect(&:underscore)
157 170 if group_items and group_items.is_a? Array
158 171 layout = @user.pref[:my_page_layout] || {}
159 172 # remove group blocks if they are presents in other groups
160 173 %w(top left right).each {|f|
161 174 layout[f] = (layout[f] || []) - group_items
162 175 }
163 176 layout[group] = group_items
164 177 @user.pref[:my_page_layout] = layout
165 178 @user.pref.save
166 179 end
167 180 end
168 181 render :nothing => true
169 182 end
170 183 end
@@ -1,348 +1,360
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 has_one :api_token, :dependent => :destroy, :class_name => 'Token', :conditions => "action='api'"
42 43 belongs_to :auth_source
43 44
44 45 # Active non-anonymous users scope
45 46 named_scope :active, :conditions => "#{User.table_name}.status = #{STATUS_ACTIVE}"
46 47
47 48 acts_as_customizable
48 49
49 50 attr_accessor :password, :password_confirmation
50 51 attr_accessor :last_before_login_on
51 52 # Prevents unauthorized assignments
52 53 attr_protected :login, :admin, :password, :password_confirmation, :hashed_password, :group_ids
53 54
54 55 validates_presence_of :login, :firstname, :lastname, :mail, :if => Proc.new { |user| !user.is_a?(AnonymousUser) }
55 56 validates_uniqueness_of :login, :if => Proc.new { |user| !user.login.blank? }
56 57 validates_uniqueness_of :mail, :if => Proc.new { |user| !user.mail.blank? }, :case_sensitive => false
57 58 # Login must contain lettres, numbers, underscores only
58 59 validates_format_of :login, :with => /^[a-z0-9_\-@\.]*$/i
59 60 validates_length_of :login, :maximum => 30
60 61 validates_format_of :firstname, :lastname, :with => /^[\w\s\'\-\.]*$/i
61 62 validates_length_of :firstname, :lastname, :maximum => 30
62 63 validates_format_of :mail, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :allow_nil => true
63 64 validates_length_of :mail, :maximum => 60, :allow_nil => true
64 65 validates_confirmation_of :password, :allow_nil => true
65 66
66 67 def before_create
67 68 self.mail_notification = false
68 69 true
69 70 end
70 71
71 72 def before_save
72 73 # update hashed_password if password was set
73 74 self.hashed_password = User.hash_password(self.password) if self.password
74 75 end
75 76
76 77 def reload(*args)
77 78 @name = nil
78 79 super
79 80 end
80 81
81 82 def identity_url=(url)
82 83 if url.blank?
83 84 write_attribute(:identity_url, '')
84 85 else
85 86 begin
86 87 write_attribute(:identity_url, OpenIdAuthentication.normalize_identifier(url))
87 88 rescue OpenIdAuthentication::InvalidOpenId
88 89 # Invlaid url, don't save
89 90 end
90 91 end
91 92 self.read_attribute(:identity_url)
92 93 end
93 94
94 95 # Returns the user that matches provided login and password, or nil
95 96 def self.try_to_login(login, password)
96 97 # Make sure no one can sign in with an empty password
97 98 return nil if password.to_s.empty?
98 99 user = find(:first, :conditions => ["login=?", login])
99 100 if user
100 101 # user is already in local database
101 102 return nil if !user.active?
102 103 if user.auth_source
103 104 # user has an external authentication method
104 105 return nil unless user.auth_source.authenticate(login, password)
105 106 else
106 107 # authentication with local password
107 108 return nil unless User.hash_password(password) == user.hashed_password
108 109 end
109 110 else
110 111 # user is not yet registered, try to authenticate with available sources
111 112 attrs = AuthSource.authenticate(login, password)
112 113 if attrs
113 114 user = new(*attrs)
114 115 user.login = login
115 116 user.language = Setting.default_language
116 117 if user.save
117 118 user.reload
118 119 logger.info("User '#{user.login}' created from the LDAP") if logger
119 120 end
120 121 end
121 122 end
122 123 user.update_attribute(:last_login_on, Time.now) if user && !user.new_record?
123 124 user
124 125 rescue => text
125 126 raise text
126 127 end
127 128
128 129 # Returns the user who matches the given autologin +key+ or nil
129 130 def self.try_to_autologin(key)
130 131 tokens = Token.find_all_by_action_and_value('autologin', key)
131 132 # Make sure there's only 1 token that matches the key
132 133 if tokens.size == 1
133 134 token = tokens.first
134 135 if (token.created_on > Setting.autologin.to_i.day.ago) && token.user && token.user.active?
135 136 token.user.update_attribute(:last_login_on, Time.now)
136 137 token.user
137 138 end
138 139 end
139 140 end
140 141
141 142 # Return user's full name for display
142 143 def name(formatter = nil)
143 144 if formatter
144 145 eval('"' + (USER_FORMATS[formatter] || USER_FORMATS[:firstname_lastname]) + '"')
145 146 else
146 147 @name ||= eval('"' + (USER_FORMATS[Setting.user_format] || USER_FORMATS[:firstname_lastname]) + '"')
147 148 end
148 149 end
149 150
150 151 def active?
151 152 self.status == STATUS_ACTIVE
152 153 end
153 154
154 155 def registered?
155 156 self.status == STATUS_REGISTERED
156 157 end
157 158
158 159 def locked?
159 160 self.status == STATUS_LOCKED
160 161 end
161 162
162 163 def check_password?(clear_password)
163 164 User.hash_password(clear_password) == self.hashed_password
164 165 end
165 166
166 167 # Generate and set a random password. Useful for automated user creation
167 168 # Based on Token#generate_token_value
168 169 #
169 170 def random_password
170 171 chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
171 172 password = ''
172 173 40.times { |i| password << chars[rand(chars.size-1)] }
173 174 self.password = password
174 175 self.password_confirmation = password
175 176 self
176 177 end
177 178
178 179 def pref
179 180 self.preference ||= UserPreference.new(:user => self)
180 181 end
181 182
182 183 def time_zone
183 184 @time_zone ||= (self.pref.time_zone.blank? ? nil : ActiveSupport::TimeZone[self.pref.time_zone])
184 185 end
185 186
186 187 def wants_comments_in_reverse_order?
187 188 self.pref[:comments_sorting] == 'desc'
188 189 end
189 190
190 191 # Return user's RSS key (a 40 chars long string), used to access feeds
191 192 def rss_key
192 193 token = self.rss_token || Token.create(:user => self, :action => 'feeds')
193 194 token.value
194 195 end
196
197 # Return user's API key (a 40 chars long string), used to access the API
198 def api_key
199 token = self.api_token || Token.create(:user => self, :action => 'api')
200 token.value
201 end
195 202
196 203 # Return an array of project ids for which the user has explicitly turned mail notifications on
197 204 def notified_projects_ids
198 205 @notified_projects_ids ||= memberships.select {|m| m.mail_notification?}.collect(&:project_id)
199 206 end
200 207
201 208 def notified_project_ids=(ids)
202 209 Member.update_all("mail_notification = #{connection.quoted_false}", ['user_id = ?', id])
203 210 Member.update_all("mail_notification = #{connection.quoted_true}", ['user_id = ? AND project_id IN (?)', id, ids]) if ids && !ids.empty?
204 211 @notified_projects_ids = nil
205 212 notified_projects_ids
206 213 end
207 214
208 215 def self.find_by_rss_key(key)
209 216 token = Token.find_by_value(key)
210 217 token && token.user.active? ? token.user : nil
211 218 end
212 219
220 def self.find_by_api_key(key)
221 token = Token.find_by_action_and_value('api', key)
222 token && token.user.active? ? token.user : nil
223 end
224
213 225 # Makes find_by_mail case-insensitive
214 226 def self.find_by_mail(mail)
215 227 find(:first, :conditions => ["LOWER(mail) = ?", mail.to_s.downcase])
216 228 end
217 229
218 230 def to_s
219 231 name
220 232 end
221 233
222 234 # Returns the current day according to user's time zone
223 235 def today
224 236 if time_zone.nil?
225 237 Date.today
226 238 else
227 239 Time.now.in_time_zone(time_zone).to_date
228 240 end
229 241 end
230 242
231 243 def logged?
232 244 true
233 245 end
234 246
235 247 def anonymous?
236 248 !logged?
237 249 end
238 250
239 251 # Return user's roles for project
240 252 def roles_for_project(project)
241 253 roles = []
242 254 # No role on archived projects
243 255 return roles unless project && project.active?
244 256 if logged?
245 257 # Find project membership
246 258 membership = memberships.detect {|m| m.project_id == project.id}
247 259 if membership
248 260 roles = membership.roles
249 261 else
250 262 @role_non_member ||= Role.non_member
251 263 roles << @role_non_member
252 264 end
253 265 else
254 266 @role_anonymous ||= Role.anonymous
255 267 roles << @role_anonymous
256 268 end
257 269 roles
258 270 end
259 271
260 272 # Return true if the user is a member of project
261 273 def member_of?(project)
262 274 !roles_for_project(project).detect {|role| role.member?}.nil?
263 275 end
264 276
265 277 # Return true if the user is allowed to do the specified action on project
266 278 # action can be:
267 279 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
268 280 # * a permission Symbol (eg. :edit_project)
269 281 def allowed_to?(action, project, options={})
270 282 if project
271 283 # No action allowed on archived projects
272 284 return false unless project.active?
273 285 # No action allowed on disabled modules
274 286 return false unless project.allows_to?(action)
275 287 # Admin users are authorized for anything else
276 288 return true if admin?
277 289
278 290 roles = roles_for_project(project)
279 291 return false unless roles
280 292 roles.detect {|role| (project.is_public? || role.member?) && role.allowed_to?(action)}
281 293
282 294 elsif options[:global]
283 295 # Admin users are always authorized
284 296 return true if admin?
285 297
286 298 # authorize if user has at least one role that has this permission
287 299 roles = memberships.collect {|m| m.roles}.flatten.uniq
288 300 roles.detect {|r| r.allowed_to?(action)} || (self.logged? ? Role.non_member.allowed_to?(action) : Role.anonymous.allowed_to?(action))
289 301 else
290 302 false
291 303 end
292 304 end
293 305
294 306 def self.current=(user)
295 307 @current_user = user
296 308 end
297 309
298 310 def self.current
299 311 @current_user ||= User.anonymous
300 312 end
301 313
302 314 # Returns the anonymous user. If the anonymous user does not exist, it is created. There can be only
303 315 # one anonymous user per database.
304 316 def self.anonymous
305 317 anonymous_user = AnonymousUser.find(:first)
306 318 if anonymous_user.nil?
307 319 anonymous_user = AnonymousUser.create(:lastname => 'Anonymous', :firstname => '', :mail => '', :login => '', :status => 0)
308 320 raise 'Unable to create the anonymous user.' if anonymous_user.new_record?
309 321 end
310 322 anonymous_user
311 323 end
312 324
313 325 protected
314 326
315 327 def validate
316 328 # Password length validation based on setting
317 329 if !password.nil? && password.size < Setting.password_min_length.to_i
318 330 errors.add(:password, :too_short, :count => Setting.password_min_length.to_i)
319 331 end
320 332 end
321 333
322 334 private
323 335
324 336 # Return password digest
325 337 def self.hash_password(clear_password)
326 338 Digest::SHA1.hexdigest(clear_password || "")
327 339 end
328 340 end
329 341
330 342 class AnonymousUser < User
331 343
332 344 def validate_on_create
333 345 # There should be only one AnonymousUser in the database
334 346 errors.add_to_base 'An anonymous user already exists.' if AnonymousUser.find(:first)
335 347 end
336 348
337 349 def available_custom_fields
338 350 []
339 351 end
340 352
341 353 # Overrides a few properties
342 354 def logged?; false end
343 355 def admin; false end
344 356 def name(*args); I18n.t(:label_user_anonymous) end
345 357 def mail; nil end
346 358 def time_zone; nil end
347 359 def rss_key; nil end
348 360 end
@@ -1,8 +1,26
1 1 <h3><%=l(:label_my_account)%></h3>
2 2
3 3 <p><%=l(:field_login)%>: <strong><%= @user.login %></strong><br />
4 4 <%=l(:field_created_on)%>: <%= format_time(@user.created_on) %></p>
5
6
7 <h4><%= l(:label_feeds_access_key) %></h4>
8
9 <p>
5 10 <% if @user.rss_token %>
6 <p><%= l(:label_feeds_access_key_created_on, distance_of_time_in_words(Time.now, @user.rss_token.created_on)) %>
7 (<%= link_to l(:button_reset), {:action => 'reset_rss_key'}, :method => :post %>)</p>
11 <%= l(:label_feeds_access_key_created_on, distance_of_time_in_words(Time.now, @user.rss_token.created_on)) %>
12 <% else %>
13 <%= l(:label_missing_feeds_access_key) %>
14 <% end %>
15 (<%= link_to l(:button_reset), {:action => 'reset_rss_key'}, :method => :post %>)
16 </p>
17
18 <h4><%= l(:label_api_access_key) %></h4>
19 <p>
20 <% if @user.api_token %>
21 <%= l(:label_api_access_key_created_on, distance_of_time_in_words(Time.now, @user.api_token.created_on)) %>
22 <% else %>
23 <%= l(:label_missing_api_access_key) %>
8 24 <% end %>
25 (<%= link_to l(:button_reset), {:action => 'reset_api_key'}, :method => :post %>)
26 </p>
@@ -1,61 +1,73
1 1 <div class="contextual">
2 2 <%= link_to(l(:button_change_password), :action => 'password') unless @user.auth_source_id %>
3 3 <%= call_hook(:view_my_account_contextual, :user => @user)%>
4 4 </div>
5 5 <h2><%=l(:label_my_account)%></h2>
6 6 <%= error_messages_for 'user' %>
7 7
8 8 <% form_for :user, @user, :url => { :action => "account" },
9 9 :builder => TabularFormBuilder,
10 10 :lang => current_language,
11 11 :html => { :id => 'my_account_form' } do |f| %>
12 12 <div class="splitcontentleft">
13 13 <h3><%=l(:label_information_plural)%></h3>
14 14 <div class="box tabular">
15 15 <p><%= f.text_field :firstname, :required => true %></p>
16 16 <p><%= f.text_field :lastname, :required => true %></p>
17 17 <p><%= f.text_field :mail, :required => true %></p>
18 18 <p><%= f.select :language, lang_options_for_select %></p>
19 19 <% if Setting.openid? %>
20 20 <p><%= f.text_field :identity_url %></p>
21 21 <% end %>
22 22
23 23 <% @user.custom_field_values.select(&:editable?).each do |value| %>
24 24 <p><%= custom_field_tag_with_label :user, value %></p>
25 25 <% end %>
26 26 <%= call_hook(:view_my_account, :user => @user, :form => f) %>
27 27 </div>
28 28
29 29 <%= submit_tag l(:button_save) %>
30 30 </div>
31 31
32 32 <div class="splitcontentright">
33 33 <h3><%=l(:field_mail_notification)%></h3>
34 34 <div class="box">
35 35 <%= select_tag 'notification_option', options_for_select(@notification_options, @notification_option),
36 36 :onchange => 'if ($("notification_option").value == "selected") {Element.show("notified-projects")} else {Element.hide("notified-projects")}' %>
37 37 <% content_tag 'div', :id => 'notified-projects', :style => (@notification_option == 'selected' ? '' : 'display:none;') do %>
38 38 <p><% User.current.projects.each do |project| %>
39 39 <label><%= check_box_tag 'notified_project_ids[]', project.id, @user.notified_projects_ids.include?(project.id) %> <%=h project.name %></label><br />
40 40 <% end %></p>
41 41 <p><em><%= l(:text_user_mail_option) %></em></p>
42 42 <% end %>
43 43 <p><label><%= check_box_tag 'no_self_notified', 1, @user.pref[:no_self_notified] %> <%= l(:label_user_mail_no_self_notified) %></label></p>
44 44 </div>
45 45
46 46 <h3><%=l(:label_preferences)%></h3>
47 47 <div class="box tabular">
48 48 <% fields_for :pref, @user.pref, :builder => TabularFormBuilder, :lang => current_language do |pref_fields| %>
49 49 <p><%= pref_fields.check_box :hide_mail %></p>
50 50 <p><%= pref_fields.select :time_zone, ActiveSupport::TimeZone.all.collect {|z| [ z.to_s, z.name ]}, :include_blank => true %></p>
51 51 <p><%= pref_fields.select :comments_sorting, [[l(:label_chronological_order), 'asc'], [l(:label_reverse_chronological_order), 'desc']] %></p>
52 52 <% end %>
53 53 </div>
54
55 <% if @user.api_token %>
56 <h3><%=l(:label_api_access_key) %></h3>
57 <div class="box">
58 <p>
59 <%= link_to_function(l(:text_show), "$('api-access-key').show();")%>
60 <pre id='api-access-key'><%= @user.api_key %></pre>
61 </p>
62 <%= javascript_tag("$('api-access-key').hide();") %>
63 </div>
64 <% end %>
65
54 66 </div>
55 67 <% end %>
56 68
57 69 <% content_for :sidebar do %>
58 70 <%= render :partial => 'sidebar' %>
59 71 <% end %>
60 72
61 73 <% html_title(l(:label_my_account)) -%>
@@ -1,865 +1,872
1 1 en:
2 2 date:
3 3 formats:
4 4 # Use the strftime parameters for formats.
5 5 # When no format has been given, it uses default.
6 6 # You can provide other formats here if you like!
7 7 default: "%m/%d/%Y"
8 8 short: "%b %d"
9 9 long: "%B %d, %Y"
10 10
11 11 day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
12 12 abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
13 13
14 14 # Don't forget the nil at the beginning; there's no such thing as a 0th month
15 15 month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
16 16 abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
17 17 # Used in date_select and datime_select.
18 18 order: [ :year, :month, :day ]
19 19
20 20 time:
21 21 formats:
22 22 default: "%m/%d/%Y %I:%M %p"
23 23 time: "%I:%M %p"
24 24 short: "%d %b %H:%M"
25 25 long: "%B %d, %Y %H:%M"
26 26 am: "am"
27 27 pm: "pm"
28 28
29 29 datetime:
30 30 distance_in_words:
31 31 half_a_minute: "half a minute"
32 32 less_than_x_seconds:
33 33 one: "less than 1 second"
34 34 other: "less than {{count}} seconds"
35 35 x_seconds:
36 36 one: "1 second"
37 37 other: "{{count}} seconds"
38 38 less_than_x_minutes:
39 39 one: "less than a minute"
40 40 other: "less than {{count}} minutes"
41 41 x_minutes:
42 42 one: "1 minute"
43 43 other: "{{count}} minutes"
44 44 about_x_hours:
45 45 one: "about 1 hour"
46 46 other: "about {{count}} hours"
47 47 x_days:
48 48 one: "1 day"
49 49 other: "{{count}} days"
50 50 about_x_months:
51 51 one: "about 1 month"
52 52 other: "about {{count}} months"
53 53 x_months:
54 54 one: "1 month"
55 55 other: "{{count}} months"
56 56 about_x_years:
57 57 one: "about 1 year"
58 58 other: "about {{count}} years"
59 59 over_x_years:
60 60 one: "over 1 year"
61 61 other: "over {{count}} years"
62 62
63 63 number:
64 64 human:
65 65 format:
66 66 delimiter: ""
67 67 precision: 1
68 68 storage_units:
69 69 format: "%n %u"
70 70 units:
71 71 byte:
72 72 one: "Byte"
73 73 other: "Bytes"
74 74 kb: "KB"
75 75 mb: "MB"
76 76 gb: "GB"
77 77 tb: "TB"
78 78
79 79
80 80 # Used in array.to_sentence.
81 81 support:
82 82 array:
83 83 sentence_connector: "and"
84 84 skip_last_comma: false
85 85
86 86 activerecord:
87 87 errors:
88 88 messages:
89 89 inclusion: "is not included in the list"
90 90 exclusion: "is reserved"
91 91 invalid: "is invalid"
92 92 confirmation: "doesn't match confirmation"
93 93 accepted: "must be accepted"
94 94 empty: "can't be empty"
95 95 blank: "can't be blank"
96 96 too_long: "is too long (maximum is {{count}} characters)"
97 97 too_short: "is too short (minimum is {{count}} characters)"
98 98 wrong_length: "is the wrong length (should be {{count}} characters)"
99 99 taken: "has already been taken"
100 100 not_a_number: "is not a number"
101 101 not_a_date: "is not a valid date"
102 102 greater_than: "must be greater than {{count}}"
103 103 greater_than_or_equal_to: "must be greater than or equal to {{count}}"
104 104 equal_to: "must be equal to {{count}}"
105 105 less_than: "must be less than {{count}}"
106 106 less_than_or_equal_to: "must be less than or equal to {{count}}"
107 107 odd: "must be odd"
108 108 even: "must be even"
109 109 greater_than_start_date: "must be greater than start date"
110 110 not_same_project: "doesn't belong to the same project"
111 111 circular_dependency: "This relation would create a circular dependency"
112 112
113 113 actionview_instancetag_blank_option: Please select
114 114
115 115 general_text_No: 'No'
116 116 general_text_Yes: 'Yes'
117 117 general_text_no: 'no'
118 118 general_text_yes: 'yes'
119 119 general_lang_name: 'English'
120 120 general_csv_separator: ','
121 121 general_csv_decimal_separator: '.'
122 122 general_csv_encoding: ISO-8859-1
123 123 general_pdf_encoding: ISO-8859-1
124 124 general_first_day_of_week: '7'
125 125
126 126 notice_account_updated: Account was successfully updated.
127 127 notice_account_invalid_creditentials: Invalid user or password
128 128 notice_account_password_updated: Password was successfully updated.
129 129 notice_account_wrong_password: Wrong password
130 130 notice_account_register_done: Account was successfully created. To activate your account, click on the link that was emailed to you.
131 131 notice_account_unknown_email: Unknown user.
132 132 notice_can_t_change_password: This account uses an external authentication source. Impossible to change the password.
133 133 notice_account_lost_email_sent: An email with instructions to choose a new password has been sent to you.
134 134 notice_account_activated: Your account has been activated. You can now log in.
135 135 notice_successful_create: Successful creation.
136 136 notice_successful_update: Successful update.
137 137 notice_successful_delete: Successful deletion.
138 138 notice_successful_connection: Successful connection.
139 139 notice_file_not_found: The page you were trying to access doesn't exist or has been removed.
140 140 notice_locking_conflict: Data has been updated by another user.
141 141 notice_not_authorized: You are not authorized to access this page.
142 142 notice_email_sent: "An email was sent to {{value}}"
143 143 notice_email_error: "An error occurred while sending mail ({{value}})"
144 144 notice_feeds_access_key_reseted: Your RSS access key was reset.
145 notice_api_access_key_reseted: Your API access key was reset.
145 146 notice_failed_to_save_issues: "Failed to save {{count}} issue(s) on {{total}} selected: {{ids}}."
146 147 notice_no_issue_selected: "No issue is selected! Please, check the issues you want to edit."
147 148 notice_account_pending: "Your account was created and is now pending administrator approval."
148 149 notice_default_data_loaded: Default configuration successfully loaded.
149 150 notice_unable_delete_version: Unable to delete version.
150 151 notice_issue_done_ratios_updated: Issue done ratios updated.
151 152
152 153 error_can_t_load_default_data: "Default configuration could not be loaded: {{value}}"
153 154 error_scm_not_found: "The entry or revision was not found in the repository."
154 155 error_scm_command_failed: "An error occurred when trying to access the repository: {{value}}"
155 156 error_scm_annotate: "The entry does not exist or can not be annotated."
156 157 error_issue_not_found_in_project: 'The issue was not found or does not belong to this project'
157 158 error_no_tracker_in_project: 'No tracker is associated to this project. Please check the Project settings.'
158 159 error_no_default_issue_status: 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
159 160 error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version can not be reopened'
160 161 error_can_not_archive_project: This project can not be archived
161 162 error_issue_done_ratios_not_updated: "Issue done ratios not updated."
162 163 error_workflow_copy_source: 'Please select a source tracker or role'
163 164 error_workflow_copy_target: 'Please select target tracker(s) and role(s)'
164 165
165 166 warning_attachments_not_saved: "{{count}} file(s) could not be saved."
166 167
167 168 mail_subject_lost_password: "Your {{value}} password"
168 169 mail_body_lost_password: 'To change your password, click on the following link:'
169 170 mail_subject_register: "Your {{value}} account activation"
170 171 mail_body_register: 'To activate your account, click on the following link:'
171 172 mail_body_account_information_external: "You can use your {{value}} account to log in."
172 173 mail_body_account_information: Your account information
173 174 mail_subject_account_activation_request: "{{value}} account activation request"
174 175 mail_body_account_activation_request: "A new user ({{value}}) has registered. The account is pending your approval:"
175 176 mail_subject_reminder: "{{count}} issue(s) due in the next days"
176 177 mail_body_reminder: "{{count}} issue(s) that are assigned to you are due in the next {{days}} days:"
177 178 mail_subject_wiki_content_added: "'{{page}}' wiki page has been added"
178 179 mail_body_wiki_content_added: "The '{{page}}' wiki page has been added by {{author}}."
179 180 mail_subject_wiki_content_updated: "'{{page}}' wiki page has been updated"
180 181 mail_body_wiki_content_updated: "The '{{page}}' wiki page has been updated by {{author}}."
181 182
182 183 gui_validation_error: 1 error
183 184 gui_validation_error_plural: "{{count}} errors"
184 185
185 186 field_name: Name
186 187 field_description: Description
187 188 field_summary: Summary
188 189 field_is_required: Required
189 190 field_firstname: Firstname
190 191 field_lastname: Lastname
191 192 field_mail: Email
192 193 field_filename: File
193 194 field_filesize: Size
194 195 field_downloads: Downloads
195 196 field_author: Author
196 197 field_created_on: Created
197 198 field_updated_on: Updated
198 199 field_field_format: Format
199 200 field_is_for_all: For all projects
200 201 field_possible_values: Possible values
201 202 field_regexp: Regular expression
202 203 field_min_length: Minimum length
203 204 field_max_length: Maximum length
204 205 field_value: Value
205 206 field_category: Category
206 207 field_title: Title
207 208 field_project: Project
208 209 field_issue: Issue
209 210 field_status: Status
210 211 field_notes: Notes
211 212 field_is_closed: Issue closed
212 213 field_is_default: Default value
213 214 field_tracker: Tracker
214 215 field_subject: Subject
215 216 field_due_date: Due date
216 217 field_assigned_to: Assigned to
217 218 field_priority: Priority
218 219 field_fixed_version: Target version
219 220 field_user: User
220 221 field_role: Role
221 222 field_homepage: Homepage
222 223 field_is_public: Public
223 224 field_parent: Subproject of
224 225 field_is_in_roadmap: Issues displayed in roadmap
225 226 field_login: Login
226 227 field_mail_notification: Email notifications
227 228 field_admin: Administrator
228 229 field_last_login_on: Last connection
229 230 field_language: Language
230 231 field_effective_date: Date
231 232 field_password: Password
232 233 field_new_password: New password
233 234 field_password_confirmation: Confirmation
234 235 field_version: Version
235 236 field_type: Type
236 237 field_host: Host
237 238 field_port: Port
238 239 field_account: Account
239 240 field_base_dn: Base DN
240 241 field_attr_login: Login attribute
241 242 field_attr_firstname: Firstname attribute
242 243 field_attr_lastname: Lastname attribute
243 244 field_attr_mail: Email attribute
244 245 field_onthefly: On-the-fly user creation
245 246 field_start_date: Start
246 247 field_done_ratio: % Done
247 248 field_auth_source: Authentication mode
248 249 field_hide_mail: Hide my email address
249 250 field_comments: Comment
250 251 field_url: URL
251 252 field_start_page: Start page
252 253 field_subproject: Subproject
253 254 field_hours: Hours
254 255 field_activity: Activity
255 256 field_spent_on: Date
256 257 field_identifier: Identifier
257 258 field_is_filter: Used as a filter
258 259 field_issue_to: Related issue
259 260 field_delay: Delay
260 261 field_assignable: Issues can be assigned to this role
261 262 field_redirect_existing_links: Redirect existing links
262 263 field_estimated_hours: Estimated time
263 264 field_column_names: Columns
264 265 field_time_zone: Time zone
265 266 field_searchable: Searchable
266 267 field_default_value: Default value
267 268 field_comments_sorting: Display comments
268 269 field_parent_title: Parent page
269 270 field_editable: Editable
270 271 field_watcher: Watcher
271 272 field_identity_url: OpenID URL
272 273 field_content: Content
273 274 field_group_by: Group results by
274 275 field_sharing: Sharing
275 276
276 277 setting_app_title: Application title
277 278 setting_app_subtitle: Application subtitle
278 279 setting_welcome_text: Welcome text
279 280 setting_default_language: Default language
280 281 setting_login_required: Authentication required
281 282 setting_self_registration: Self-registration
282 283 setting_attachment_max_size: Attachment max. size
283 284 setting_issues_export_limit: Issues export limit
284 285 setting_mail_from: Emission email address
285 286 setting_bcc_recipients: Blind carbon copy recipients (bcc)
286 287 setting_plain_text_mail: Plain text mail (no HTML)
287 288 setting_host_name: Host name and path
288 289 setting_text_formatting: Text formatting
289 290 setting_wiki_compression: Wiki history compression
290 291 setting_feeds_limit: Feed content limit
291 292 setting_default_projects_public: New projects are public by default
292 293 setting_autofetch_changesets: Autofetch commits
293 294 setting_sys_api_enabled: Enable WS for repository management
294 295 setting_commit_ref_keywords: Referencing keywords
295 296 setting_commit_fix_keywords: Fixing keywords
296 297 setting_autologin: Autologin
297 298 setting_date_format: Date format
298 299 setting_time_format: Time format
299 300 setting_cross_project_issue_relations: Allow cross-project issue relations
300 301 setting_issue_list_default_columns: Default columns displayed on the issue list
301 302 setting_repositories_encodings: Repositories encodings
302 303 setting_commit_logs_encoding: Commit messages encoding
303 304 setting_emails_footer: Emails footer
304 305 setting_protocol: Protocol
305 306 setting_per_page_options: Objects per page options
306 307 setting_user_format: Users display format
307 308 setting_activity_days_default: Days displayed on project activity
308 309 setting_display_subprojects_issues: Display subprojects issues on main projects by default
309 310 setting_enabled_scm: Enabled SCM
310 311 setting_mail_handler_api_enabled: Enable WS for incoming emails
311 312 setting_mail_handler_api_key: API key
312 313 setting_sequential_project_identifiers: Generate sequential project identifiers
313 314 setting_gravatar_enabled: Use Gravatar user icons
314 315 setting_gravatar_default: Default Gravatar image
315 316 setting_diff_max_lines_displayed: Max number of diff lines displayed
316 317 setting_file_max_size_displayed: Max size of text files displayed inline
317 318 setting_repository_log_display_limit: Maximum number of revisions displayed on file log
318 319 setting_openid: Allow OpenID login and registration
319 320 setting_password_min_length: Minimum password length
320 321 setting_new_project_user_role_id: Role given to a non-admin user who creates a project
321 322 setting_default_projects_modules: Default enabled modules for new projects
322 323 setting_issue_done_ratio: Calculate the issue done ratio with
323 324 setting_issue_done_ratio_issue_field: Use the issue field
324 325 setting_issue_done_ratio_issue_status: Use the issue status
325 326 setting_start_of_week: Start calendars on
326 327
327 328 permission_add_project: Create project
328 329 permission_edit_project: Edit project
329 330 permission_select_project_modules: Select project modules
330 331 permission_manage_members: Manage members
331 332 permission_manage_versions: Manage versions
332 333 permission_manage_categories: Manage issue categories
333 334 permission_view_issues: View Issues
334 335 permission_add_issues: Add issues
335 336 permission_edit_issues: Edit issues
336 337 permission_manage_issue_relations: Manage issue relations
337 338 permission_add_issue_notes: Add notes
338 339 permission_edit_issue_notes: Edit notes
339 340 permission_edit_own_issue_notes: Edit own notes
340 341 permission_move_issues: Move issues
341 342 permission_delete_issues: Delete issues
342 343 permission_manage_public_queries: Manage public queries
343 344 permission_save_queries: Save queries
344 345 permission_view_gantt: View gantt chart
345 346 permission_view_calendar: View calendar
346 347 permission_view_issue_watchers: View watchers list
347 348 permission_add_issue_watchers: Add watchers
348 349 permission_delete_issue_watchers: Delete watchers
349 350 permission_log_time: Log spent time
350 351 permission_view_time_entries: View spent time
351 352 permission_edit_time_entries: Edit time logs
352 353 permission_edit_own_time_entries: Edit own time logs
353 354 permission_manage_news: Manage news
354 355 permission_comment_news: Comment news
355 356 permission_manage_documents: Manage documents
356 357 permission_view_documents: View documents
357 358 permission_manage_files: Manage files
358 359 permission_view_files: View files
359 360 permission_manage_wiki: Manage wiki
360 361 permission_rename_wiki_pages: Rename wiki pages
361 362 permission_delete_wiki_pages: Delete wiki pages
362 363 permission_view_wiki_pages: View wiki
363 364 permission_view_wiki_edits: View wiki history
364 365 permission_edit_wiki_pages: Edit wiki pages
365 366 permission_delete_wiki_pages_attachments: Delete attachments
366 367 permission_protect_wiki_pages: Protect wiki pages
367 368 permission_manage_repository: Manage repository
368 369 permission_browse_repository: Browse repository
369 370 permission_view_changesets: View changesets
370 371 permission_commit_access: Commit access
371 372 permission_manage_boards: Manage boards
372 373 permission_view_messages: View messages
373 374 permission_add_messages: Post messages
374 375 permission_edit_messages: Edit messages
375 376 permission_edit_own_messages: Edit own messages
376 377 permission_delete_messages: Delete messages
377 378 permission_delete_own_messages: Delete own messages
378 379
379 380 project_module_issue_tracking: Issue tracking
380 381 project_module_time_tracking: Time tracking
381 382 project_module_news: News
382 383 project_module_documents: Documents
383 384 project_module_files: Files
384 385 project_module_wiki: Wiki
385 386 project_module_repository: Repository
386 387 project_module_boards: Boards
387 388
388 389 label_user: User
389 390 label_user_plural: Users
390 391 label_user_new: New user
391 392 label_user_anonymous: Anonymous
392 393 label_project: Project
393 394 label_project_new: New project
394 395 label_project_plural: Projects
395 396 label_x_projects:
396 397 zero: no projects
397 398 one: 1 project
398 399 other: "{{count}} projects"
399 400 label_project_all: All Projects
400 401 label_project_latest: Latest projects
401 402 label_issue: Issue
402 403 label_issue_new: New issue
403 404 label_issue_plural: Issues
404 405 label_issue_view_all: View all issues
405 406 label_issues_by: "Issues by {{value}}"
406 407 label_issue_added: Issue added
407 408 label_issue_updated: Issue updated
408 409 label_document: Document
409 410 label_document_new: New document
410 411 label_document_plural: Documents
411 412 label_document_added: Document added
412 413 label_role: Role
413 414 label_role_plural: Roles
414 415 label_role_new: New role
415 416 label_role_and_permissions: Roles and permissions
416 417 label_member: Member
417 418 label_member_new: New member
418 419 label_member_plural: Members
419 420 label_tracker: Tracker
420 421 label_tracker_plural: Trackers
421 422 label_tracker_new: New tracker
422 423 label_workflow: Workflow
423 424 label_issue_status: Issue status
424 425 label_issue_status_plural: Issue statuses
425 426 label_issue_status_new: New status
426 427 label_issue_category: Issue category
427 428 label_issue_category_plural: Issue categories
428 429 label_issue_category_new: New category
429 430 label_custom_field: Custom field
430 431 label_custom_field_plural: Custom fields
431 432 label_custom_field_new: New custom field
432 433 label_enumerations: Enumerations
433 434 label_enumeration_new: New value
434 435 label_information: Information
435 436 label_information_plural: Information
436 437 label_please_login: Please log in
437 438 label_register: Register
438 439 label_login_with_open_id_option: or login with OpenID
439 440 label_password_lost: Lost password
440 441 label_home: Home
441 442 label_my_page: My page
442 443 label_my_account: My account
443 444 label_my_projects: My projects
444 445 label_administration: Administration
445 446 label_login: Sign in
446 447 label_logout: Sign out
447 448 label_help: Help
448 449 label_reported_issues: Reported issues
449 450 label_assigned_to_me_issues: Issues assigned to me
450 451 label_last_login: Last connection
451 452 label_registered_on: Registered on
452 453 label_activity: Activity
453 454 label_overall_activity: Overall activity
454 455 label_user_activity: "{{value}}'s activity"
455 456 label_new: New
456 457 label_logged_as: Logged in as
457 458 label_environment: Environment
458 459 label_authentication: Authentication
459 460 label_auth_source: Authentication mode
460 461 label_auth_source_new: New authentication mode
461 462 label_auth_source_plural: Authentication modes
462 463 label_subproject_plural: Subprojects
463 464 label_and_its_subprojects: "{{value}} and its subprojects"
464 465 label_min_max_length: Min - Max length
465 466 label_list: List
466 467 label_date: Date
467 468 label_integer: Integer
468 469 label_float: Float
469 470 label_boolean: Boolean
470 471 label_string: Text
471 472 label_text: Long text
472 473 label_attribute: Attribute
473 474 label_attribute_plural: Attributes
474 475 label_download: "{{count}} Download"
475 476 label_download_plural: "{{count}} Downloads"
476 477 label_no_data: No data to display
477 478 label_change_status: Change status
478 479 label_history: History
479 480 label_attachment: File
480 481 label_attachment_new: New file
481 482 label_attachment_delete: Delete file
482 483 label_attachment_plural: Files
483 484 label_file_added: File added
484 485 label_report: Report
485 486 label_report_plural: Reports
486 487 label_news: News
487 488 label_news_new: Add news
488 489 label_news_plural: News
489 490 label_news_latest: Latest news
490 491 label_news_view_all: View all news
491 492 label_news_added: News added
492 493 label_settings: Settings
493 494 label_overview: Overview
494 495 label_version: Version
495 496 label_version_new: New version
496 497 label_version_plural: Versions
497 498 label_confirmation: Confirmation
498 499 label_export_to: 'Also available in:'
499 500 label_read: Read...
500 501 label_public_projects: Public projects
501 502 label_open_issues: open
502 503 label_open_issues_plural: open
503 504 label_closed_issues: closed
504 505 label_closed_issues_plural: closed
505 506 label_x_open_issues_abbr_on_total:
506 507 zero: 0 open / {{total}}
507 508 one: 1 open / {{total}}
508 509 other: "{{count}} open / {{total}}"
509 510 label_x_open_issues_abbr:
510 511 zero: 0 open
511 512 one: 1 open
512 513 other: "{{count}} open"
513 514 label_x_closed_issues_abbr:
514 515 zero: 0 closed
515 516 one: 1 closed
516 517 other: "{{count}} closed"
517 518 label_total: Total
518 519 label_permissions: Permissions
519 520 label_current_status: Current status
520 521 label_new_statuses_allowed: New statuses allowed
521 522 label_all: all
522 523 label_none: none
523 524 label_nobody: nobody
524 525 label_next: Next
525 526 label_previous: Previous
526 527 label_used_by: Used by
527 528 label_details: Details
528 529 label_add_note: Add a note
529 530 label_per_page: Per page
530 531 label_calendar: Calendar
531 532 label_months_from: months from
532 533 label_gantt: Gantt
533 534 label_internal: Internal
534 535 label_last_changes: "last {{count}} changes"
535 536 label_change_view_all: View all changes
536 537 label_personalize_page: Personalize this page
537 538 label_comment: Comment
538 539 label_comment_plural: Comments
539 540 label_x_comments:
540 541 zero: no comments
541 542 one: 1 comment
542 543 other: "{{count}} comments"
543 544 label_comment_add: Add a comment
544 545 label_comment_added: Comment added
545 546 label_comment_delete: Delete comments
546 547 label_query: Custom query
547 548 label_query_plural: Custom queries
548 549 label_query_new: New query
549 550 label_filter_add: Add filter
550 551 label_filter_plural: Filters
551 552 label_equals: is
552 553 label_not_equals: is not
553 554 label_in_less_than: in less than
554 555 label_in_more_than: in more than
555 556 label_greater_or_equal: '>='
556 557 label_less_or_equal: '<='
557 558 label_in: in
558 559 label_today: today
559 560 label_all_time: all time
560 561 label_yesterday: yesterday
561 562 label_this_week: this week
562 563 label_last_week: last week
563 564 label_last_n_days: "last {{count}} days"
564 565 label_this_month: this month
565 566 label_last_month: last month
566 567 label_this_year: this year
567 568 label_date_range: Date range
568 569 label_less_than_ago: less than days ago
569 570 label_more_than_ago: more than days ago
570 571 label_ago: days ago
571 572 label_contains: contains
572 573 label_not_contains: doesn't contain
573 574 label_day_plural: days
574 575 label_repository: Repository
575 576 label_repository_plural: Repositories
576 577 label_browse: Browse
577 578 label_modification: "{{count}} change"
578 579 label_modification_plural: "{{count}} changes"
579 580 label_branch: Branch
580 581 label_tag: Tag
581 582 label_revision: Revision
582 583 label_revision_plural: Revisions
583 584 label_revision_id: "Revision {{value}}"
584 585 label_associated_revisions: Associated revisions
585 586 label_added: added
586 587 label_modified: modified
587 588 label_copied: copied
588 589 label_renamed: renamed
589 590 label_deleted: deleted
590 591 label_latest_revision: Latest revision
591 592 label_latest_revision_plural: Latest revisions
592 593 label_view_revisions: View revisions
593 594 label_view_all_revisions: View all revisions
594 595 label_max_size: Maximum size
595 596 label_sort_highest: Move to top
596 597 label_sort_higher: Move up
597 598 label_sort_lower: Move down
598 599 label_sort_lowest: Move to bottom
599 600 label_roadmap: Roadmap
600 601 label_roadmap_due_in: "Due in {{value}}"
601 602 label_roadmap_overdue: "{{value}} late"
602 603 label_roadmap_no_issues: No issues for this version
603 604 label_search: Search
604 605 label_result_plural: Results
605 606 label_all_words: All words
606 607 label_wiki: Wiki
607 608 label_wiki_edit: Wiki edit
608 609 label_wiki_edit_plural: Wiki edits
609 610 label_wiki_page: Wiki page
610 611 label_wiki_page_plural: Wiki pages
611 612 label_index_by_title: Index by title
612 613 label_index_by_date: Index by date
613 614 label_current_version: Current version
614 615 label_preview: Preview
615 616 label_feed_plural: Feeds
616 617 label_changes_details: Details of all changes
617 618 label_issue_tracking: Issue tracking
618 619 label_spent_time: Spent time
619 620 label_f_hour: "{{value}} hour"
620 621 label_f_hour_plural: "{{value}} hours"
621 622 label_time_tracking: Time tracking
622 623 label_change_plural: Changes
623 624 label_statistics: Statistics
624 625 label_commits_per_month: Commits per month
625 626 label_commits_per_author: Commits per author
626 627 label_view_diff: View differences
627 628 label_diff_inline: inline
628 629 label_diff_side_by_side: side by side
629 630 label_options: Options
630 631 label_copy_workflow_from: Copy workflow from
631 632 label_permissions_report: Permissions report
632 633 label_watched_issues: Watched issues
633 634 label_related_issues: Related issues
634 635 label_applied_status: Applied status
635 636 label_loading: Loading...
636 637 label_relation_new: New relation
637 638 label_relation_delete: Delete relation
638 639 label_relates_to: related to
639 640 label_duplicates: duplicates
640 641 label_duplicated_by: duplicated by
641 642 label_blocks: blocks
642 643 label_blocked_by: blocked by
643 644 label_precedes: precedes
644 645 label_follows: follows
645 646 label_end_to_start: end to start
646 647 label_end_to_end: end to end
647 648 label_start_to_start: start to start
648 649 label_start_to_end: start to end
649 650 label_stay_logged_in: Stay logged in
650 651 label_disabled: disabled
651 652 label_show_completed_versions: Show completed versions
652 653 label_me: me
653 654 label_board: Forum
654 655 label_board_new: New forum
655 656 label_board_plural: Forums
656 657 label_topic_plural: Topics
657 658 label_message_plural: Messages
658 659 label_message_last: Last message
659 660 label_message_new: New message
660 661 label_message_posted: Message added
661 662 label_reply_plural: Replies
662 663 label_send_information: Send account information to the user
663 664 label_year: Year
664 665 label_month: Month
665 666 label_week: Week
666 667 label_date_from: From
667 668 label_date_to: To
668 669 label_language_based: Based on user's language
669 670 label_sort_by: "Sort by {{value}}"
670 671 label_send_test_email: Send a test email
672 label_feeds_access_key: RSS access key
673 label_missing_feeds_access_key: Missing a RSS access key
671 674 label_feeds_access_key_created_on: "RSS access key created {{value}} ago"
672 675 label_module_plural: Modules
673 676 label_added_time_by: "Added by {{author}} {{age}} ago"
674 677 label_updated_time_by: "Updated by {{author}} {{age}} ago"
675 678 label_updated_time: "Updated {{value}} ago"
676 679 label_jump_to_a_project: Jump to a project...
677 680 label_file_plural: Files
678 681 label_changeset_plural: Changesets
679 682 label_default_columns: Default columns
680 683 label_no_change_option: (No change)
681 684 label_bulk_edit_selected_issues: Bulk edit selected issues
682 685 label_theme: Theme
683 686 label_default: Default
684 687 label_search_titles_only: Search titles only
685 688 label_user_mail_option_all: "For any event on all my projects"
686 689 label_user_mail_option_selected: "For any event on the selected projects only..."
687 690 label_user_mail_option_none: "Only for things I watch or I'm involved in"
688 691 label_user_mail_no_self_notified: "I don't want to be notified of changes that I make myself"
689 692 label_registration_activation_by_email: account activation by email
690 693 label_registration_manual_activation: manual account activation
691 694 label_registration_automatic_activation: automatic account activation
692 695 label_display_per_page: "Per page: {{value}}"
693 696 label_age: Age
694 697 label_change_properties: Change properties
695 698 label_general: General
696 699 label_more: More
697 700 label_scm: SCM
698 701 label_plugins: Plugins
699 702 label_ldap_authentication: LDAP authentication
700 703 label_downloads_abbr: D/L
701 704 label_optional_description: Optional description
702 705 label_add_another_file: Add another file
703 706 label_preferences: Preferences
704 707 label_chronological_order: In chronological order
705 708 label_reverse_chronological_order: In reverse chronological order
706 709 label_planning: Planning
707 710 label_incoming_emails: Incoming emails
708 711 label_generate_key: Generate a key
709 712 label_issue_watchers: Watchers
710 713 label_example: Example
711 714 label_display: Display
712 715 label_sort: Sort
713 716 label_ascending: Ascending
714 717 label_descending: Descending
715 718 label_date_from_to: From {{start}} to {{end}}
716 719 label_wiki_content_added: Wiki page added
717 720 label_wiki_content_updated: Wiki page updated
718 721 label_group: Group
719 722 label_group_plural: Groups
720 723 label_group_new: New group
721 724 label_time_entry_plural: Spent time
722 725 label_version_sharing_none: Not shared
723 726 label_version_sharing_descendants: With subprojects
724 727 label_version_sharing_hierarchy: With project hierarchy
725 728 label_version_sharing_tree: With project tree
726 729 label_version_sharing_system: With all projects
727 730 label_update_issue_done_ratios: Update issue done ratios
728 731 label_copy_source: Source
729 732 label_copy_target: Target
730 733 label_copy_same_as_target: Same as target
731 734 label_display_used_statuses_only: Only display statuses that are used by this tracker
735 label_api_access_key: API access key
736 label_missing_api_access_key: Missing an API access key
737 label_api_access_key_created_on: "API access key created {{value}} ago"
732 738
733 739 button_login: Login
734 740 button_submit: Submit
735 741 button_save: Save
736 742 button_check_all: Check all
737 743 button_uncheck_all: Uncheck all
738 744 button_delete: Delete
739 745 button_create: Create
740 746 button_create_and_continue: Create and continue
741 747 button_test: Test
742 748 button_edit: Edit
743 749 button_add: Add
744 750 button_change: Change
745 751 button_apply: Apply
746 752 button_clear: Clear
747 753 button_lock: Lock
748 754 button_unlock: Unlock
749 755 button_download: Download
750 756 button_list: List
751 757 button_view: View
752 758 button_move: Move
753 759 button_move_and_follow: Move and follow
754 760 button_back: Back
755 761 button_cancel: Cancel
756 762 button_activate: Activate
757 763 button_sort: Sort
758 764 button_log_time: Log time
759 765 button_rollback: Rollback to this version
760 766 button_watch: Watch
761 767 button_unwatch: Unwatch
762 768 button_reply: Reply
763 769 button_archive: Archive
764 770 button_unarchive: Unarchive
765 771 button_reset: Reset
766 772 button_rename: Rename
767 773 button_change_password: Change password
768 774 button_copy: Copy
769 775 button_copy_and_follow: Copy and follow
770 776 button_annotate: Annotate
771 777 button_update: Update
772 778 button_configure: Configure
773 779 button_quote: Quote
774 780 button_duplicate: Duplicate
775 781
776 782 status_active: active
777 783 status_registered: registered
778 784 status_locked: locked
779 785
780 786 version_status_open: open
781 787 version_status_locked: locked
782 788 version_status_closed: closed
783 789
784 790 field_active: Active
785 791
786 792 text_select_mail_notifications: Select actions for which email notifications should be sent.
787 793 text_regexp_info: eg. ^[A-Z0-9]+$
788 794 text_min_max_length_info: 0 means no restriction
789 795 text_project_destroy_confirmation: Are you sure you want to delete this project and related data ?
790 796 text_subprojects_destroy_warning: "Its subproject(s): {{value}} will be also deleted."
791 797 text_workflow_edit: Select a role and a tracker to edit the workflow
792 798 text_are_you_sure: Are you sure ?
793 799 text_journal_changed: "{{label}} changed from {{old}} to {{new}}"
794 800 text_journal_set_to: "{{label}} set to {{value}}"
795 801 text_journal_deleted: "{{label}} deleted ({{old}})"
796 802 text_journal_added: "{{label}} {{value}} added"
797 803 text_tip_task_begin_day: task beginning this day
798 804 text_tip_task_end_day: task ending this day
799 805 text_tip_task_begin_end_day: task beginning and ending this day
800 806 text_project_identifier_info: 'Only lower case letters (a-z), numbers and dashes are allowed.<br />Once saved, the identifier can not be changed.'
801 807 text_caracters_maximum: "{{count}} characters maximum."
802 808 text_caracters_minimum: "Must be at least {{count}} characters long."
803 809 text_length_between: "Length between {{min}} and {{max}} characters."
804 810 text_tracker_no_workflow: No workflow defined for this tracker
805 811 text_unallowed_characters: Unallowed characters
806 812 text_comma_separated: Multiple values allowed (comma separated).
807 813 text_issues_ref_in_commit_messages: Referencing and fixing issues in commit messages
808 814 text_issue_added: "Issue {{id}} has been reported by {{author}}."
809 815 text_issue_updated: "Issue {{id}} has been updated by {{author}}."
810 816 text_wiki_destroy_confirmation: Are you sure you want to delete this wiki and all its content ?
811 817 text_issue_category_destroy_question: "Some issues ({{count}}) are assigned to this category. What do you want to do ?"
812 818 text_issue_category_destroy_assignments: Remove category assignments
813 819 text_issue_category_reassign_to: Reassign issues to this category
814 820 text_user_mail_option: "For unselected projects, you will only receive notifications about things you watch or you're involved in (eg. issues you're the author or assignee)."
815 821 text_no_configuration_data: "Roles, trackers, issue statuses and workflow have not been configured yet.\nIt is highly recommended to load the default configuration. You will be able to modify it once loaded."
816 822 text_load_default_configuration: Load the default configuration
817 823 text_status_changed_by_changeset: "Applied in changeset {{value}}."
818 824 text_issues_destroy_confirmation: 'Are you sure you want to delete the selected issue(s) ?'
819 825 text_select_project_modules: 'Select modules to enable for this project:'
820 826 text_default_administrator_account_changed: Default administrator account changed
821 827 text_file_repository_writable: Attachments directory writable
822 828 text_plugin_assets_writable: Plugin assets directory writable
823 829 text_rmagick_available: RMagick available (optional)
824 830 text_destroy_time_entries_question: "{{hours}} hours were reported on the issues you are about to delete. What do you want to do ?"
825 831 text_destroy_time_entries: Delete reported hours
826 832 text_assign_time_entries_to_project: Assign reported hours to the project
827 833 text_reassign_time_entries: 'Reassign reported hours to this issue:'
828 834 text_user_wrote: "{{value}} wrote:"
829 835 text_enumeration_destroy_question: "{{count}} objects are assigned to this value."
830 836 text_enumeration_category_reassign_to: 'Reassign them to this value:'
831 837 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
832 838 text_repository_usernames_mapping: "Select or update the Redmine user mapped to each username found in the repository log.\nUsers with the same Redmine and repository username or email are automatically mapped."
833 839 text_diff_truncated: '... This diff was truncated because it exceeds the maximum size that can be displayed.'
834 840 text_custom_field_possible_values_info: 'One line for each value'
835 841 text_wiki_page_destroy_question: "This page has {{descendants}} child page(s) and descendant(s). What do you want to do?"
836 842 text_wiki_page_nullify_children: "Keep child pages as root pages"
837 843 text_wiki_page_destroy_children: "Delete child pages and all their descendants"
838 844 text_wiki_page_reassign_children: "Reassign child pages to this parent page"
845 text_show: Show
839 846
840 847 default_role_manager: Manager
841 848 default_role_developper: Developer
842 849 default_role_reporter: Reporter
843 850 default_tracker_bug: Bug
844 851 default_tracker_feature: Feature
845 852 default_tracker_support: Support
846 853 default_issue_status_new: New
847 854 default_issue_status_in_progress: In Progress
848 855 default_issue_status_resolved: Resolved
849 856 default_issue_status_feedback: Feedback
850 857 default_issue_status_closed: Closed
851 858 default_issue_status_rejected: Rejected
852 859 default_doc_category_user: User documentation
853 860 default_doc_category_tech: Technical documentation
854 861 default_priority_low: Low
855 862 default_priority_normal: Normal
856 863 default_priority_high: High
857 864 default_priority_urgent: Urgent
858 865 default_priority_immediate: Immediate
859 866 default_activity_design: Design
860 867 default_activity_development: Development
861 868
862 869 enumeration_issue_priorities: Issue priorities
863 870 enumeration_doc_categories: Document categories
864 871 enumeration_activities: Activities (time tracking)
865 872 enumeration_system_activity: System Activity
@@ -1,166 +1,200
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 require File.dirname(__FILE__) + '/../test_helper'
19 19 require 'my_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class MyController; def rescue_action(e) raise e end; end
23 23
24 24 class MyControllerTest < ActionController::TestCase
25 25 fixtures :users, :user_preferences, :roles, :projects, :issues, :issue_statuses, :trackers, :enumerations, :custom_fields
26 26
27 27 def setup
28 28 @controller = MyController.new
29 29 @request = ActionController::TestRequest.new
30 30 @request.session[:user_id] = 2
31 31 @response = ActionController::TestResponse.new
32 32 end
33 33
34 34 def test_index
35 35 get :index
36 36 assert_response :success
37 37 assert_template 'page'
38 38 end
39 39
40 40 def test_page
41 41 get :page
42 42 assert_response :success
43 43 assert_template 'page'
44 44 end
45 45
46 46 def test_my_account_should_show_editable_custom_fields
47 47 get :account
48 48 assert_response :success
49 49 assert_template 'account'
50 50 assert_equal User.find(2), assigns(:user)
51 51
52 52 assert_tag :input, :attributes => { :name => 'user[custom_field_values][4]'}
53 53 end
54 54
55 55 def test_my_account_should_not_show_non_editable_custom_fields
56 56 UserCustomField.find(4).update_attribute :editable, false
57 57
58 58 get :account
59 59 assert_response :success
60 60 assert_template 'account'
61 61 assert_equal User.find(2), assigns(:user)
62 62
63 63 assert_no_tag :input, :attributes => { :name => 'user[custom_field_values][4]'}
64 64 end
65 65
66 66 def test_update_account
67 67 post :account, :user => {:firstname => "Joe",
68 68 :login => "root",
69 69 :admin => 1,
70 70 :custom_field_values => {"4" => "0100562500"}}
71 71 assert_redirected_to 'my/account'
72 72 user = User.find(2)
73 73 assert_equal user, assigns(:user)
74 74 assert_equal "Joe", user.firstname
75 75 assert_equal "jsmith", user.login
76 76 assert_equal "0100562500", user.custom_value_for(4).value
77 77 assert !user.admin?
78 78 end
79 79
80 80 def test_change_password
81 81 get :password
82 82 assert_response :success
83 83 assert_template 'password'
84 84
85 85 # non matching password confirmation
86 86 post :password, :password => 'jsmith',
87 87 :new_password => 'hello',
88 88 :new_password_confirmation => 'hello2'
89 89 assert_response :success
90 90 assert_template 'password'
91 91 assert_tag :tag => "div", :attributes => { :class => "errorExplanation" }
92 92
93 93 # wrong password
94 94 post :password, :password => 'wrongpassword',
95 95 :new_password => 'hello',
96 96 :new_password_confirmation => 'hello'
97 97 assert_response :success
98 98 assert_template 'password'
99 99 assert_equal 'Wrong password', flash[:error]
100 100
101 101 # good password
102 102 post :password, :password => 'jsmith',
103 103 :new_password => 'hello',
104 104 :new_password_confirmation => 'hello'
105 105 assert_redirected_to 'my/account'
106 106 assert User.try_to_login('jsmith', 'hello')
107 107 end
108 108
109 109 def test_page_layout
110 110 get :page_layout
111 111 assert_response :success
112 112 assert_template 'page_layout'
113 113 end
114 114
115 115 def test_add_block
116 116 xhr :post, :add_block, :block => 'issuesreportedbyme'
117 117 assert_response :success
118 118 assert User.find(2).pref[:my_page_layout]['top'].include?('issuesreportedbyme')
119 119 end
120 120
121 121 def test_remove_block
122 122 xhr :post, :remove_block, :block => 'issuesassignedtome'
123 123 assert_response :success
124 124 assert !User.find(2).pref[:my_page_layout].values.flatten.include?('issuesassignedtome')
125 125 end
126 126
127 127 def test_order_blocks
128 128 xhr :post, :order_blocks, :group => 'left', 'list-left' => ['documents', 'calendar', 'latestnews']
129 129 assert_response :success
130 130 assert_equal ['documents', 'calendar', 'latestnews'], User.find(2).pref[:my_page_layout]['left']
131 131 end
132 132
133 133 context "POST to reset_rss_key" do
134 134 context "with an existing rss_token" do
135 135 setup do
136 136 @previous_token_value = User.find(2).rss_key # Will generate one if it's missing
137 137 post :reset_rss_key
138 138 end
139 139
140 140 should "destroy the existing token" do
141 141 assert_not_equal @previous_token_value, User.find(2).rss_key
142 142 end
143 143
144 144 should "create a new token" do
145 145 assert User.find(2).rss_token
146 146 end
147 147
148 148 should_set_the_flash_to /reset/
149 149 should_redirect_to('my account') {'/my/account' }
150 150 end
151 151
152 152 context "with no rss_token" do
153 153 setup do
154 154 assert_nil User.find(2).rss_token
155 155 post :reset_rss_key
156 156 end
157 157
158 158 should "create a new token" do
159 159 assert User.find(2).rss_token
160 160 end
161 161
162 162 should_set_the_flash_to /reset/
163 163 should_redirect_to('my account') {'/my/account' }
164 164 end
165 165 end
166
167 context "POST to reset_api_key" do
168 context "with an existing api_token" do
169 setup do
170 @previous_token_value = User.find(2).api_key # Will generate one if it's missing
171 post :reset_api_key
172 end
173
174 should "destroy the existing token" do
175 assert_not_equal @previous_token_value, User.find(2).api_key
176 end
177
178 should "create a new token" do
179 assert User.find(2).api_token
180 end
181
182 should_set_the_flash_to /reset/
183 should_redirect_to('my account') {'/my/account' }
184 end
185
186 context "with no api_token" do
187 setup do
188 assert_nil User.find(2).api_token
189 post :reset_api_key
190 end
191
192 should "create a new token" do
193 assert User.find(2).api_token
194 end
195
196 should_set_the_flash_to /reset/
197 should_redirect_to('my account') {'/my/account' }
198 end
199 end
166 200 end
@@ -1,229 +1,279
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 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 File.dirname(__FILE__) + '/../test_helper'
19 19
20 20 class UserTest < ActiveSupport::TestCase
21 21 fixtures :users, :members, :projects, :roles, :member_roles
22 22
23 23 def setup
24 24 @admin = User.find(1)
25 25 @jsmith = User.find(2)
26 26 @dlopper = User.find(3)
27 27 end
28 28
29 29 test 'object_daddy creation' do
30 30 User.generate_with_protected!(:firstname => 'Testing connection')
31 31 User.generate_with_protected!(:firstname => 'Testing connection')
32 32 assert_equal 2, User.count(:all, :conditions => {:firstname => 'Testing connection'})
33 33 end
34 34
35 35 def test_truth
36 36 assert_kind_of User, @jsmith
37 37 end
38 38
39 39 def test_create
40 40 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
41 41
42 42 user.login = "jsmith"
43 43 user.password, user.password_confirmation = "password", "password"
44 44 # login uniqueness
45 45 assert !user.save
46 46 assert_equal 1, user.errors.count
47 47
48 48 user.login = "newuser"
49 49 user.password, user.password_confirmation = "passwd", "password"
50 50 # password confirmation
51 51 assert !user.save
52 52 assert_equal 1, user.errors.count
53 53
54 54 user.password, user.password_confirmation = "password", "password"
55 55 assert user.save
56 56 end
57 57
58 58 def test_mail_uniqueness_should_not_be_case_sensitive
59 59 u = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
60 60 u.login = 'newuser1'
61 61 u.password, u.password_confirmation = "password", "password"
62 62 assert u.save
63 63
64 64 u = User.new(:firstname => "new", :lastname => "user", :mail => "newUser@Somenet.foo")
65 65 u.login = 'newuser2'
66 66 u.password, u.password_confirmation = "password", "password"
67 67 assert !u.save
68 68 assert_equal I18n.translate('activerecord.errors.messages.taken'), u.errors.on(:mail)
69 69 end
70 70
71 71 def test_update
72 72 assert_equal "admin", @admin.login
73 73 @admin.login = "john"
74 74 assert @admin.save, @admin.errors.full_messages.join("; ")
75 75 @admin.reload
76 76 assert_equal "john", @admin.login
77 77 end
78 78
79 79 def test_destroy
80 80 User.find(2).destroy
81 81 assert_nil User.find_by_id(2)
82 82 assert Member.find_all_by_user_id(2).empty?
83 83 end
84 84
85 85 def test_validate
86 86 @admin.login = ""
87 87 assert !@admin.save
88 88 assert_equal 1, @admin.errors.count
89 89 end
90 90
91 91 def test_password
92 92 user = User.try_to_login("admin", "admin")
93 93 assert_kind_of User, user
94 94 assert_equal "admin", user.login
95 95 user.password = "hello"
96 96 assert user.save
97 97
98 98 user = User.try_to_login("admin", "hello")
99 99 assert_kind_of User, user
100 100 assert_equal "admin", user.login
101 101 assert_equal User.hash_password("hello"), user.hashed_password
102 102 end
103 103
104 104 def test_name_format
105 105 assert_equal 'Smith, John', @jsmith.name(:lastname_coma_firstname)
106 106 Setting.user_format = :firstname_lastname
107 107 assert_equal 'John Smith', @jsmith.reload.name
108 108 Setting.user_format = :username
109 109 assert_equal 'jsmith', @jsmith.reload.name
110 110 end
111 111
112 112 def test_lock
113 113 user = User.try_to_login("jsmith", "jsmith")
114 114 assert_equal @jsmith, user
115 115
116 116 @jsmith.status = User::STATUS_LOCKED
117 117 assert @jsmith.save
118 118
119 119 user = User.try_to_login("jsmith", "jsmith")
120 120 assert_equal nil, user
121 121 end
122 122
123 123 def test_create_anonymous
124 124 AnonymousUser.delete_all
125 125 anon = User.anonymous
126 126 assert !anon.new_record?
127 127 assert_kind_of AnonymousUser, anon
128 128 end
129
129
130 should_have_one :rss_token
131
130 132 def test_rss_key
131 133 assert_nil @jsmith.rss_token
132 134 key = @jsmith.rss_key
133 135 assert_equal 40, key.length
134 136
135 137 @jsmith.reload
136 138 assert_equal key, @jsmith.rss_key
137 139 end
140
138 141
142 should_have_one :api_token
143
144 context "User#api_key" do
145 should "generate a new one if the user doesn't have one" do
146 user = User.generate_with_protected!(:api_token => nil)
147 assert_nil user.api_token
148
149 key = user.api_key
150 assert_equal 40, key.length
151 user.reload
152 assert_equal key, user.api_key
153 end
154
155 should "return the existing api token value" do
156 user = User.generate_with_protected!
157 token = Token.generate!(:action => 'api')
158 user.api_token = token
159 assert user.save
160
161 assert_equal token.value, user.api_key
162 end
163 end
164
165 context "User#find_by_api_key" do
166 should "return nil if no matching key is found" do
167 assert_nil User.find_by_api_key('zzzzzzzzz')
168 end
169
170 should "return nil if the key is found for an inactive user" do
171 user = User.generate_with_protected!(:status => User::STATUS_LOCKED)
172 token = Token.generate!(:action => 'api')
173 user.api_token = token
174 user.save
175
176 assert_nil User.find_by_api_key(token.value)
177 end
178
179 should "return the user if the key is found for an active user" do
180 user = User.generate_with_protected!(:status => User::STATUS_ACTIVE)
181 token = Token.generate!(:action => 'api')
182 user.api_token = token
183 user.save
184
185 assert_equal user, User.find_by_api_key(token.value)
186 end
187 end
188
139 189 def test_roles_for_project
140 190 # user with a role
141 191 roles = @jsmith.roles_for_project(Project.find(1))
142 192 assert_kind_of Role, roles.first
143 193 assert_equal "Manager", roles.first.name
144 194
145 195 # user with no role
146 196 assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?}
147 197 end
148 198
149 199 def test_mail_notification_all
150 200 @jsmith.mail_notification = true
151 201 @jsmith.notified_project_ids = []
152 202 @jsmith.save
153 203 @jsmith.reload
154 204 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
155 205 end
156 206
157 207 def test_mail_notification_selected
158 208 @jsmith.mail_notification = false
159 209 @jsmith.notified_project_ids = [1]
160 210 @jsmith.save
161 211 @jsmith.reload
162 212 assert Project.find(1).recipients.include?(@jsmith.mail)
163 213 end
164 214
165 215 def test_mail_notification_none
166 216 @jsmith.mail_notification = false
167 217 @jsmith.notified_project_ids = []
168 218 @jsmith.save
169 219 @jsmith.reload
170 220 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
171 221 end
172 222
173 223 def test_comments_sorting_preference
174 224 assert !@jsmith.wants_comments_in_reverse_order?
175 225 @jsmith.pref.comments_sorting = 'asc'
176 226 assert !@jsmith.wants_comments_in_reverse_order?
177 227 @jsmith.pref.comments_sorting = 'desc'
178 228 assert @jsmith.wants_comments_in_reverse_order?
179 229 end
180 230
181 231 def test_find_by_mail_should_be_case_insensitive
182 232 u = User.find_by_mail('JSmith@somenet.foo')
183 233 assert_not_nil u
184 234 assert_equal 'jsmith@somenet.foo', u.mail
185 235 end
186 236
187 237 def test_random_password
188 238 u = User.new
189 239 u.random_password
190 240 assert !u.password.blank?
191 241 assert !u.password_confirmation.blank?
192 242 end
193 243
194 244 if Object.const_defined?(:OpenID)
195 245
196 246 def test_setting_identity_url
197 247 normalized_open_id_url = 'http://example.com/'
198 248 u = User.new( :identity_url => 'http://example.com/' )
199 249 assert_equal normalized_open_id_url, u.identity_url
200 250 end
201 251
202 252 def test_setting_identity_url_without_trailing_slash
203 253 normalized_open_id_url = 'http://example.com/'
204 254 u = User.new( :identity_url => 'http://example.com' )
205 255 assert_equal normalized_open_id_url, u.identity_url
206 256 end
207 257
208 258 def test_setting_identity_url_without_protocol
209 259 normalized_open_id_url = 'http://example.com/'
210 260 u = User.new( :identity_url => 'example.com' )
211 261 assert_equal normalized_open_id_url, u.identity_url
212 262 end
213 263
214 264 def test_setting_blank_identity_url
215 265 u = User.new( :identity_url => 'example.com' )
216 266 u.identity_url = ''
217 267 assert u.identity_url.blank?
218 268 end
219 269
220 270 def test_setting_invalid_identity_url
221 271 u = User.new( :identity_url => 'this is not an openid url' )
222 272 assert u.identity_url.blank?
223 273 end
224 274
225 275 else
226 276 puts "Skipping openid tests."
227 277 end
228 278
229 279 end
General Comments 0
You need to be logged in to leave comments. Login now