##// END OF EJS Templates
AccountController#show (/account/show/:id) moved to UsersController#show (/users/:id)....
Jean-Philippe Lang -
r2874:a842769c3f38
parent child
Show More
@@ -1,283 +1,262
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class AccountController < ApplicationController
18 class AccountController < ApplicationController
19 helper :custom_fields
19 helper :custom_fields
20 include CustomFieldsHelper
20 include CustomFieldsHelper
21
21
22 # prevents login action to be filtered by check_if_login_required application scope filter
22 # prevents login action to be filtered by check_if_login_required application scope filter
23 skip_before_filter :check_if_login_required, :only => [:login, :lost_password, :register, :activate]
23 skip_before_filter :check_if_login_required
24
25 # Show user's account
26 def show
27 @user = User.active.find(params[:id])
28 @custom_values = @user.custom_values
29
30 # show only public projects and private projects that the logged in user is also a member of
31 @memberships = @user.memberships.select do |membership|
32 membership.project.is_public? || (User.current.member_of?(membership.project))
33 end
34
35 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
36 @events_by_day = events.group_by(&:event_date)
37
38 if @user != User.current && !User.current.admin? && @memberships.empty? && events.empty?
39 render_404 and return
40 end
41
42 rescue ActiveRecord::RecordNotFound
43 render_404
44 end
45
24
46 # Login request and validation
25 # Login request and validation
47 def login
26 def login
48 if request.get?
27 if request.get?
49 # Logout user
28 # Logout user
50 self.logged_user = nil
29 self.logged_user = nil
51 else
30 else
52 # Authenticate user
31 # Authenticate user
53 if Setting.openid? && using_open_id?
32 if Setting.openid? && using_open_id?
54 open_id_authenticate(params[:openid_url])
33 open_id_authenticate(params[:openid_url])
55 else
34 else
56 password_authentication
35 password_authentication
57 end
36 end
58 end
37 end
59 end
38 end
60
39
61 # Log out current user and redirect to welcome page
40 # Log out current user and redirect to welcome page
62 def logout
41 def logout
63 cookies.delete :autologin
42 cookies.delete :autologin
64 Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) if User.current.logged?
43 Token.delete_all(["user_id = ? AND action = ?", User.current.id, 'autologin']) if User.current.logged?
65 self.logged_user = nil
44 self.logged_user = nil
66 redirect_to home_url
45 redirect_to home_url
67 end
46 end
68
47
69 # Enable user to choose a new password
48 # Enable user to choose a new password
70 def lost_password
49 def lost_password
71 redirect_to(home_url) && return unless Setting.lost_password?
50 redirect_to(home_url) && return unless Setting.lost_password?
72 if params[:token]
51 if params[:token]
73 @token = Token.find_by_action_and_value("recovery", params[:token])
52 @token = Token.find_by_action_and_value("recovery", params[:token])
74 redirect_to(home_url) && return unless @token and !@token.expired?
53 redirect_to(home_url) && return unless @token and !@token.expired?
75 @user = @token.user
54 @user = @token.user
76 if request.post?
55 if request.post?
77 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
56 @user.password, @user.password_confirmation = params[:new_password], params[:new_password_confirmation]
78 if @user.save
57 if @user.save
79 @token.destroy
58 @token.destroy
80 flash[:notice] = l(:notice_account_password_updated)
59 flash[:notice] = l(:notice_account_password_updated)
81 redirect_to :action => 'login'
60 redirect_to :action => 'login'
82 return
61 return
83 end
62 end
84 end
63 end
85 render :template => "account/password_recovery"
64 render :template => "account/password_recovery"
86 return
65 return
87 else
66 else
88 if request.post?
67 if request.post?
89 user = User.find_by_mail(params[:mail])
68 user = User.find_by_mail(params[:mail])
90 # user not found in db
69 # user not found in db
91 flash.now[:error] = l(:notice_account_unknown_email) and return unless user
70 flash.now[:error] = l(:notice_account_unknown_email) and return unless user
92 # user uses an external authentification
71 # user uses an external authentification
93 flash.now[:error] = l(:notice_can_t_change_password) and return if user.auth_source_id
72 flash.now[:error] = l(:notice_can_t_change_password) and return if user.auth_source_id
94 # create a new token for password recovery
73 # create a new token for password recovery
95 token = Token.new(:user => user, :action => "recovery")
74 token = Token.new(:user => user, :action => "recovery")
96 if token.save
75 if token.save
97 Mailer.deliver_lost_password(token)
76 Mailer.deliver_lost_password(token)
98 flash[:notice] = l(:notice_account_lost_email_sent)
77 flash[:notice] = l(:notice_account_lost_email_sent)
99 redirect_to :action => 'login'
78 redirect_to :action => 'login'
100 return
79 return
101 end
80 end
102 end
81 end
103 end
82 end
104 end
83 end
105
84
106 # User self-registration
85 # User self-registration
107 def register
86 def register
108 redirect_to(home_url) && return unless Setting.self_registration? || session[:auth_source_registration]
87 redirect_to(home_url) && return unless Setting.self_registration? || session[:auth_source_registration]
109 if request.get?
88 if request.get?
110 session[:auth_source_registration] = nil
89 session[:auth_source_registration] = nil
111 @user = User.new(:language => Setting.default_language)
90 @user = User.new(:language => Setting.default_language)
112 else
91 else
113 @user = User.new(params[:user])
92 @user = User.new(params[:user])
114 @user.admin = false
93 @user.admin = false
115 @user.status = User::STATUS_REGISTERED
94 @user.status = User::STATUS_REGISTERED
116 if session[:auth_source_registration]
95 if session[:auth_source_registration]
117 @user.status = User::STATUS_ACTIVE
96 @user.status = User::STATUS_ACTIVE
118 @user.login = session[:auth_source_registration][:login]
97 @user.login = session[:auth_source_registration][:login]
119 @user.auth_source_id = session[:auth_source_registration][:auth_source_id]
98 @user.auth_source_id = session[:auth_source_registration][:auth_source_id]
120 if @user.save
99 if @user.save
121 session[:auth_source_registration] = nil
100 session[:auth_source_registration] = nil
122 self.logged_user = @user
101 self.logged_user = @user
123 flash[:notice] = l(:notice_account_activated)
102 flash[:notice] = l(:notice_account_activated)
124 redirect_to :controller => 'my', :action => 'account'
103 redirect_to :controller => 'my', :action => 'account'
125 end
104 end
126 else
105 else
127 @user.login = params[:user][:login]
106 @user.login = params[:user][:login]
128 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
107 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation]
129
108
130 case Setting.self_registration
109 case Setting.self_registration
131 when '1'
110 when '1'
132 register_by_email_activation(@user)
111 register_by_email_activation(@user)
133 when '3'
112 when '3'
134 register_automatically(@user)
113 register_automatically(@user)
135 else
114 else
136 register_manually_by_administrator(@user)
115 register_manually_by_administrator(@user)
137 end
116 end
138 end
117 end
139 end
118 end
140 end
119 end
141
120
142 # Token based account activation
121 # Token based account activation
143 def activate
122 def activate
144 redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
123 redirect_to(home_url) && return unless Setting.self_registration? && params[:token]
145 token = Token.find_by_action_and_value('register', params[:token])
124 token = Token.find_by_action_and_value('register', params[:token])
146 redirect_to(home_url) && return unless token and !token.expired?
125 redirect_to(home_url) && return unless token and !token.expired?
147 user = token.user
126 user = token.user
148 redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED
127 redirect_to(home_url) && return unless user.status == User::STATUS_REGISTERED
149 user.status = User::STATUS_ACTIVE
128 user.status = User::STATUS_ACTIVE
150 if user.save
129 if user.save
151 token.destroy
130 token.destroy
152 flash[:notice] = l(:notice_account_activated)
131 flash[:notice] = l(:notice_account_activated)
153 end
132 end
154 redirect_to :action => 'login'
133 redirect_to :action => 'login'
155 end
134 end
156
135
157 private
136 private
158
137
159 def password_authentication
138 def password_authentication
160 user = User.try_to_login(params[:username], params[:password])
139 user = User.try_to_login(params[:username], params[:password])
161 if user.nil?
140 if user.nil?
162 # Invalid credentials
141 # Invalid credentials
163 flash.now[:error] = l(:notice_account_invalid_creditentials)
142 flash.now[:error] = l(:notice_account_invalid_creditentials)
164 elsif user.new_record?
143 elsif user.new_record?
165 # Onthefly creation failed, display the registration form to fill/fix attributes
144 # Onthefly creation failed, display the registration form to fill/fix attributes
166 @user = user
145 @user = user
167 session[:auth_source_registration] = {:login => user.login, :auth_source_id => user.auth_source_id }
146 session[:auth_source_registration] = {:login => user.login, :auth_source_id => user.auth_source_id }
168 render :action => 'register'
147 render :action => 'register'
169 else
148 else
170 # Valid user
149 # Valid user
171 successful_authentication(user)
150 successful_authentication(user)
172 end
151 end
173 end
152 end
174
153
175
154
176 def open_id_authenticate(openid_url)
155 def open_id_authenticate(openid_url)
177 authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url) do |result, identity_url, registration|
156 authenticate_with_open_id(openid_url, :required => [:nickname, :fullname, :email], :return_to => signin_url) do |result, identity_url, registration|
178 if result.successful?
157 if result.successful?
179 user = User.find_or_initialize_by_identity_url(identity_url)
158 user = User.find_or_initialize_by_identity_url(identity_url)
180 if user.new_record?
159 if user.new_record?
181 # Self-registration off
160 # Self-registration off
182 redirect_to(home_url) && return unless Setting.self_registration?
161 redirect_to(home_url) && return unless Setting.self_registration?
183
162
184 # Create on the fly
163 # Create on the fly
185 user.login = registration['nickname'] unless registration['nickname'].nil?
164 user.login = registration['nickname'] unless registration['nickname'].nil?
186 user.mail = registration['email'] unless registration['email'].nil?
165 user.mail = registration['email'] unless registration['email'].nil?
187 user.firstname, user.lastname = registration['fullname'].split(' ') unless registration['fullname'].nil?
166 user.firstname, user.lastname = registration['fullname'].split(' ') unless registration['fullname'].nil?
188 user.random_password
167 user.random_password
189 user.status = User::STATUS_REGISTERED
168 user.status = User::STATUS_REGISTERED
190
169
191 case Setting.self_registration
170 case Setting.self_registration
192 when '1'
171 when '1'
193 register_by_email_activation(user) do
172 register_by_email_activation(user) do
194 onthefly_creation_failed(user)
173 onthefly_creation_failed(user)
195 end
174 end
196 when '3'
175 when '3'
197 register_automatically(user) do
176 register_automatically(user) do
198 onthefly_creation_failed(user)
177 onthefly_creation_failed(user)
199 end
178 end
200 else
179 else
201 register_manually_by_administrator(user) do
180 register_manually_by_administrator(user) do
202 onthefly_creation_failed(user)
181 onthefly_creation_failed(user)
203 end
182 end
204 end
183 end
205 else
184 else
206 # Existing record
185 # Existing record
207 if user.active?
186 if user.active?
208 successful_authentication(user)
187 successful_authentication(user)
209 else
188 else
210 account_pending
189 account_pending
211 end
190 end
212 end
191 end
213 end
192 end
214 end
193 end
215 end
194 end
216
195
217 def successful_authentication(user)
196 def successful_authentication(user)
218 # Valid user
197 # Valid user
219 self.logged_user = user
198 self.logged_user = user
220 # generate a key and set cookie if autologin
199 # generate a key and set cookie if autologin
221 if params[:autologin] && Setting.autologin?
200 if params[:autologin] && Setting.autologin?
222 token = Token.create(:user => user, :action => 'autologin')
201 token = Token.create(:user => user, :action => 'autologin')
223 cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
202 cookies[:autologin] = { :value => token.value, :expires => 1.year.from_now }
224 end
203 end
225 call_hook(:controller_account_success_authentication_after, {:user => user })
204 call_hook(:controller_account_success_authentication_after, {:user => user })
226 redirect_back_or_default :controller => 'my', :action => 'page'
205 redirect_back_or_default :controller => 'my', :action => 'page'
227 end
206 end
228
207
229 # Onthefly creation failed, display the registration form to fill/fix attributes
208 # Onthefly creation failed, display the registration form to fill/fix attributes
230 def onthefly_creation_failed(user, auth_source_options = { })
209 def onthefly_creation_failed(user, auth_source_options = { })
231 @user = user
210 @user = user
232 session[:auth_source_registration] = auth_source_options unless auth_source_options.empty?
211 session[:auth_source_registration] = auth_source_options unless auth_source_options.empty?
233 render :action => 'register'
212 render :action => 'register'
234 end
213 end
235
214
236 # Register a user for email activation.
215 # Register a user for email activation.
237 #
216 #
238 # Pass a block for behavior when a user fails to save
217 # Pass a block for behavior when a user fails to save
239 def register_by_email_activation(user, &block)
218 def register_by_email_activation(user, &block)
240 token = Token.new(:user => user, :action => "register")
219 token = Token.new(:user => user, :action => "register")
241 if user.save and token.save
220 if user.save and token.save
242 Mailer.deliver_register(token)
221 Mailer.deliver_register(token)
243 flash[:notice] = l(:notice_account_register_done)
222 flash[:notice] = l(:notice_account_register_done)
244 redirect_to :action => 'login'
223 redirect_to :action => 'login'
245 else
224 else
246 yield if block_given?
225 yield if block_given?
247 end
226 end
248 end
227 end
249
228
250 # Automatically register a user
229 # Automatically register a user
251 #
230 #
252 # Pass a block for behavior when a user fails to save
231 # Pass a block for behavior when a user fails to save
253 def register_automatically(user, &block)
232 def register_automatically(user, &block)
254 # Automatic activation
233 # Automatic activation
255 user.status = User::STATUS_ACTIVE
234 user.status = User::STATUS_ACTIVE
256 user.last_login_on = Time.now
235 user.last_login_on = Time.now
257 if user.save
236 if user.save
258 self.logged_user = user
237 self.logged_user = user
259 flash[:notice] = l(:notice_account_activated)
238 flash[:notice] = l(:notice_account_activated)
260 redirect_to :controller => 'my', :action => 'account'
239 redirect_to :controller => 'my', :action => 'account'
261 else
240 else
262 yield if block_given?
241 yield if block_given?
263 end
242 end
264 end
243 end
265
244
266 # Manual activation by the administrator
245 # Manual activation by the administrator
267 #
246 #
268 # Pass a block for behavior when a user fails to save
247 # Pass a block for behavior when a user fails to save
269 def register_manually_by_administrator(user, &block)
248 def register_manually_by_administrator(user, &block)
270 if user.save
249 if user.save
271 # Sends an email to the administrators
250 # Sends an email to the administrators
272 Mailer.deliver_account_activation_request(user)
251 Mailer.deliver_account_activation_request(user)
273 account_pending
252 account_pending
274 else
253 else
275 yield if block_given?
254 yield if block_given?
276 end
255 end
277 end
256 end
278
257
279 def account_pending
258 def account_pending
280 flash[:notice] = l(:notice_account_pending)
259 flash[:notice] = l(:notice_account_pending)
281 redirect_to :action => 'login'
260 redirect_to :action => 'login'
282 end
261 end
283 end
262 end
@@ -1,125 +1,145
1 # redMine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class UsersController < ApplicationController
18 class UsersController < ApplicationController
19 before_filter :require_admin
19 before_filter :require_admin, :except => :show
20
20
21 helper :sort
21 helper :sort
22 include SortHelper
22 include SortHelper
23 helper :custom_fields
23 helper :custom_fields
24 include CustomFieldsHelper
24 include CustomFieldsHelper
25
25
26 def index
26 def index
27 list
27 list
28 render :action => 'list' unless request.xhr?
28 render :action => 'list' unless request.xhr?
29 end
29 end
30
30
31 def list
31 def list
32 sort_init 'login', 'asc'
32 sort_init 'login', 'asc'
33 sort_update %w(login firstname lastname mail admin created_on last_login_on)
33 sort_update %w(login firstname lastname mail admin created_on last_login_on)
34
34
35 @status = params[:status] ? params[:status].to_i : 1
35 @status = params[:status] ? params[:status].to_i : 1
36 c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
36 c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
37
37
38 unless params[:name].blank?
38 unless params[:name].blank?
39 name = "%#{params[:name].strip.downcase}%"
39 name = "%#{params[:name].strip.downcase}%"
40 c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ? OR LOWER(mail) LIKE ?", name, name, name, name]
40 c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ? OR LOWER(mail) LIKE ?", name, name, name, name]
41 end
41 end
42
42
43 @user_count = User.count(:conditions => c.conditions)
43 @user_count = User.count(:conditions => c.conditions)
44 @user_pages = Paginator.new self, @user_count,
44 @user_pages = Paginator.new self, @user_count,
45 per_page_option,
45 per_page_option,
46 params['page']
46 params['page']
47 @users = User.find :all,:order => sort_clause,
47 @users = User.find :all,:order => sort_clause,
48 :conditions => c.conditions,
48 :conditions => c.conditions,
49 :limit => @user_pages.items_per_page,
49 :limit => @user_pages.items_per_page,
50 :offset => @user_pages.current.offset
50 :offset => @user_pages.current.offset
51
51
52 render :action => "list", :layout => false if request.xhr?
52 render :action => "list", :layout => false if request.xhr?
53 end
53 end
54
54
55 def show
56 @user = User.active.find(params[:id])
57 @custom_values = @user.custom_values
58
59 # show only public projects and private projects that the logged in user is also a member of
60 @memberships = @user.memberships.select do |membership|
61 membership.project.is_public? || (User.current.member_of?(membership.project))
62 end
63
64 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
65 @events_by_day = events.group_by(&:event_date)
66
67 if @user != User.current && !User.current.admin? && @memberships.empty? && events.empty?
68 render_404 and return
69 end
70
71 rescue ActiveRecord::RecordNotFound
72 render_404
73 end
74
55 def add
75 def add
56 if request.get?
76 if request.get?
57 @user = User.new(:language => Setting.default_language)
77 @user = User.new(:language => Setting.default_language)
58 else
78 else
59 @user = User.new(params[:user])
79 @user = User.new(params[:user])
60 @user.admin = params[:user][:admin] || false
80 @user.admin = params[:user][:admin] || false
61 @user.login = params[:user][:login]
81 @user.login = params[:user][:login]
62 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless @user.auth_source_id
82 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless @user.auth_source_id
63 if @user.save
83 if @user.save
64 Mailer.deliver_account_information(@user, params[:password]) if params[:send_information]
84 Mailer.deliver_account_information(@user, params[:password]) if params[:send_information]
65 flash[:notice] = l(:notice_successful_create)
85 flash[:notice] = l(:notice_successful_create)
66 redirect_to :controller => 'users', :action => 'edit', :id => @user
86 redirect_to :controller => 'users', :action => 'edit', :id => @user
67 end
87 end
68 end
88 end
69 @auth_sources = AuthSource.find(:all)
89 @auth_sources = AuthSource.find(:all)
70 end
90 end
71
91
72 def edit
92 def edit
73 @user = User.find(params[:id])
93 @user = User.find(params[:id])
74 if request.post?
94 if request.post?
75 @user.admin = params[:user][:admin] if params[:user][:admin]
95 @user.admin = params[:user][:admin] if params[:user][:admin]
76 @user.login = params[:user][:login] if params[:user][:login]
96 @user.login = params[:user][:login] if params[:user][:login]
77 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless params[:password].nil? or params[:password].empty? or @user.auth_source_id
97 @user.password, @user.password_confirmation = params[:password], params[:password_confirmation] unless params[:password].nil? or params[:password].empty? or @user.auth_source_id
78 @user.group_ids = params[:user][:group_ids] if params[:user][:group_ids]
98 @user.group_ids = params[:user][:group_ids] if params[:user][:group_ids]
79 @user.attributes = params[:user]
99 @user.attributes = params[:user]
80 # Was the account actived ? (do it before User#save clears the change)
100 # Was the account actived ? (do it before User#save clears the change)
81 was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
101 was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
82 if @user.save
102 if @user.save
83 if was_activated
103 if was_activated
84 Mailer.deliver_account_activated(@user)
104 Mailer.deliver_account_activated(@user)
85 elsif @user.active? && params[:send_information] && !params[:password].blank? && @user.auth_source_id.nil?
105 elsif @user.active? && params[:send_information] && !params[:password].blank? && @user.auth_source_id.nil?
86 Mailer.deliver_account_information(@user, params[:password])
106 Mailer.deliver_account_information(@user, params[:password])
87 end
107 end
88 flash[:notice] = l(:notice_successful_update)
108 flash[:notice] = l(:notice_successful_update)
89 redirect_to :back
109 redirect_to :back
90 end
110 end
91 end
111 end
92 @auth_sources = AuthSource.find(:all)
112 @auth_sources = AuthSource.find(:all)
93 @membership ||= Member.new
113 @membership ||= Member.new
94 rescue ::ActionController::RedirectBackError
114 rescue ::ActionController::RedirectBackError
95 redirect_to :controller => 'users', :action => 'edit', :id => @user
115 redirect_to :controller => 'users', :action => 'edit', :id => @user
96 end
116 end
97
117
98 def edit_membership
118 def edit_membership
99 @user = User.find(params[:id])
119 @user = User.find(params[:id])
100 @membership = params[:membership_id] ? Member.find(params[:membership_id]) : Member.new(:principal => @user)
120 @membership = params[:membership_id] ? Member.find(params[:membership_id]) : Member.new(:principal => @user)
101 @membership.attributes = params[:membership]
121 @membership.attributes = params[:membership]
102 @membership.save if request.post?
122 @membership.save if request.post?
103 respond_to do |format|
123 respond_to do |format|
104 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
124 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
105 format.js {
125 format.js {
106 render(:update) {|page|
126 render(:update) {|page|
107 page.replace_html "tab-content-memberships", :partial => 'users/memberships'
127 page.replace_html "tab-content-memberships", :partial => 'users/memberships'
108 page.visual_effect(:highlight, "member-#{@membership.id}")
128 page.visual_effect(:highlight, "member-#{@membership.id}")
109 }
129 }
110 }
130 }
111 end
131 end
112 end
132 end
113
133
114 def destroy_membership
134 def destroy_membership
115 @user = User.find(params[:id])
135 @user = User.find(params[:id])
116 @membership = Member.find(params[:membership_id])
136 @membership = Member.find(params[:membership_id])
117 if request.post? && @membership.deletable?
137 if request.post? && @membership.deletable?
118 @membership.destroy
138 @membership.destroy
119 end
139 end
120 respond_to do |format|
140 respond_to do |format|
121 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
141 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
122 format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
142 format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
123 end
143 end
124 end
144 end
125 end
145 end
@@ -1,676 +1,675
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'coderay'
18 require 'coderay'
19 require 'coderay/helpers/file_type'
19 require 'coderay/helpers/file_type'
20 require 'forwardable'
20 require 'forwardable'
21 require 'cgi'
21 require 'cgi'
22
22
23 module ApplicationHelper
23 module ApplicationHelper
24 include Redmine::WikiFormatting::Macros::Definitions
24 include Redmine::WikiFormatting::Macros::Definitions
25 include Redmine::I18n
25 include Redmine::I18n
26 include GravatarHelper::PublicMethods
26 include GravatarHelper::PublicMethods
27
27
28 extend Forwardable
28 extend Forwardable
29 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
29 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
30
30
31 # Return true if user is authorized for controller/action, otherwise false
31 # Return true if user is authorized for controller/action, otherwise false
32 def authorize_for(controller, action)
32 def authorize_for(controller, action)
33 User.current.allowed_to?({:controller => controller, :action => action}, @project)
33 User.current.allowed_to?({:controller => controller, :action => action}, @project)
34 end
34 end
35
35
36 # Display a link if user is authorized
36 # Display a link if user is authorized
37 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
37 def link_to_if_authorized(name, options = {}, html_options = nil, *parameters_for_method_reference)
38 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
38 link_to(name, options, html_options, *parameters_for_method_reference) if authorize_for(options[:controller] || params[:controller], options[:action])
39 end
39 end
40
40
41 # Display a link to remote if user is authorized
41 # Display a link to remote if user is authorized
42 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
42 def link_to_remote_if_authorized(name, options = {}, html_options = nil)
43 url = options[:url] || {}
43 url = options[:url] || {}
44 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
44 link_to_remote(name, options, html_options) if authorize_for(url[:controller] || params[:controller], url[:action])
45 end
45 end
46
46
47 # Display a link to user's account page
47 # Display a link to user's account page
48 def link_to_user(user, options={})
48 def link_to_user(user, options={})
49 if user.is_a?(User)
49 if user.is_a?(User)
50 !user.anonymous? ? link_to(user.name(options[:format]), :controller => 'account', :action => 'show', :id => user) : 'Anonymous'
50 !user.anonymous? ? link_to(user.name(options[:format]), :controller => 'users', :action => 'show', :id => user) : 'Anonymous'
51 else
51 else
52 user.to_s
52 user.to_s
53 end
53 end
54 end
54 end
55
55
56 def link_to_issue(issue, options={})
56 def link_to_issue(issue, options={})
57 options[:class] ||= issue.css_classes
57 options[:class] ||= issue.css_classes
58 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
58 link_to "#{issue.tracker.name} ##{issue.id}", {:controller => "issues", :action => "show", :id => issue}, options
59 end
59 end
60
60
61 # Generates a link to an attachment.
61 # Generates a link to an attachment.
62 # Options:
62 # Options:
63 # * :text - Link text (default to attachment filename)
63 # * :text - Link text (default to attachment filename)
64 # * :download - Force download (default: false)
64 # * :download - Force download (default: false)
65 def link_to_attachment(attachment, options={})
65 def link_to_attachment(attachment, options={})
66 text = options.delete(:text) || attachment.filename
66 text = options.delete(:text) || attachment.filename
67 action = options.delete(:download) ? 'download' : 'show'
67 action = options.delete(:download) ? 'download' : 'show'
68
68
69 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
69 link_to(h(text), {:controller => 'attachments', :action => action, :id => attachment, :filename => attachment.filename }, options)
70 end
70 end
71
71
72 def toggle_link(name, id, options={})
72 def toggle_link(name, id, options={})
73 onclick = "Element.toggle('#{id}'); "
73 onclick = "Element.toggle('#{id}'); "
74 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
74 onclick << (options[:focus] ? "Form.Element.focus('#{options[:focus]}'); " : "this.blur(); ")
75 onclick << "return false;"
75 onclick << "return false;"
76 link_to(name, "#", :onclick => onclick)
76 link_to(name, "#", :onclick => onclick)
77 end
77 end
78
78
79 def image_to_function(name, function, html_options = {})
79 def image_to_function(name, function, html_options = {})
80 html_options.symbolize_keys!
80 html_options.symbolize_keys!
81 tag(:input, html_options.merge({
81 tag(:input, html_options.merge({
82 :type => "image", :src => image_path(name),
82 :type => "image", :src => image_path(name),
83 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
83 :onclick => (html_options[:onclick] ? "#{html_options[:onclick]}; " : "") + "#{function};"
84 }))
84 }))
85 end
85 end
86
86
87 def prompt_to_remote(name, text, param, url, html_options = {})
87 def prompt_to_remote(name, text, param, url, html_options = {})
88 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
88 html_options[:onclick] = "promptToRemote('#{text}', '#{param}', '#{url_for(url)}'); return false;"
89 link_to name, {}, html_options
89 link_to name, {}, html_options
90 end
90 end
91
91
92 def format_activity_title(text)
92 def format_activity_title(text)
93 h(truncate_single_line(text, :length => 100))
93 h(truncate_single_line(text, :length => 100))
94 end
94 end
95
95
96 def format_activity_day(date)
96 def format_activity_day(date)
97 date == Date.today ? l(:label_today).titleize : format_date(date)
97 date == Date.today ? l(:label_today).titleize : format_date(date)
98 end
98 end
99
99
100 def format_activity_description(text)
100 def format_activity_description(text)
101 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
101 h(truncate(text.to_s, :length => 120).gsub(%r{[\r\n]*<(pre|code)>.*$}m, '...')).gsub(/[\r\n]+/, "<br />")
102 end
102 end
103
103
104 def due_date_distance_in_words(date)
104 def due_date_distance_in_words(date)
105 if date
105 if date
106 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
106 l((date < Date.today ? :label_roadmap_overdue : :label_roadmap_due_in), distance_of_date_in_words(Date.today, date))
107 end
107 end
108 end
108 end
109
109
110 def render_page_hierarchy(pages, node=nil)
110 def render_page_hierarchy(pages, node=nil)
111 content = ''
111 content = ''
112 if pages[node]
112 if pages[node]
113 content << "<ul class=\"pages-hierarchy\">\n"
113 content << "<ul class=\"pages-hierarchy\">\n"
114 pages[node].each do |page|
114 pages[node].each do |page|
115 content << "<li>"
115 content << "<li>"
116 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'index', :id => page.project, :page => page.title},
116 content << link_to(h(page.pretty_title), {:controller => 'wiki', :action => 'index', :id => page.project, :page => page.title},
117 :title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
117 :title => (page.respond_to?(:updated_on) ? l(:label_updated_time, distance_of_time_in_words(Time.now, page.updated_on)) : nil))
118 content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
118 content << "\n" + render_page_hierarchy(pages, page.id) if pages[page.id]
119 content << "</li>\n"
119 content << "</li>\n"
120 end
120 end
121 content << "</ul>\n"
121 content << "</ul>\n"
122 end
122 end
123 content
123 content
124 end
124 end
125
125
126 # Renders flash messages
126 # Renders flash messages
127 def render_flash_messages
127 def render_flash_messages
128 s = ''
128 s = ''
129 flash.each do |k,v|
129 flash.each do |k,v|
130 s << content_tag('div', v, :class => "flash #{k}")
130 s << content_tag('div', v, :class => "flash #{k}")
131 end
131 end
132 s
132 s
133 end
133 end
134
134
135 # Renders tabs and their content
135 # Renders tabs and their content
136 def render_tabs(tabs)
136 def render_tabs(tabs)
137 if tabs.any?
137 if tabs.any?
138 render :partial => 'common/tabs', :locals => {:tabs => tabs}
138 render :partial => 'common/tabs', :locals => {:tabs => tabs}
139 else
139 else
140 content_tag 'p', l(:label_no_data), :class => "nodata"
140 content_tag 'p', l(:label_no_data), :class => "nodata"
141 end
141 end
142 end
142 end
143
143
144 # Renders the project quick-jump box
144 # Renders the project quick-jump box
145 def render_project_jump_box
145 def render_project_jump_box
146 # Retrieve them now to avoid a COUNT query
146 # Retrieve them now to avoid a COUNT query
147 projects = User.current.projects.all
147 projects = User.current.projects.all
148 if projects.any?
148 if projects.any?
149 s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
149 s = '<select onchange="if (this.value != \'\') { window.location = this.value; }">' +
150 "<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
150 "<option value=''>#{ l(:label_jump_to_a_project) }</option>" +
151 '<option value="" disabled="disabled">---</option>'
151 '<option value="" disabled="disabled">---</option>'
152 s << project_tree_options_for_select(projects, :selected => @project) do |p|
152 s << project_tree_options_for_select(projects, :selected => @project) do |p|
153 { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
153 { :value => url_for(:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item) }
154 end
154 end
155 s << '</select>'
155 s << '</select>'
156 s
156 s
157 end
157 end
158 end
158 end
159
159
160 def project_tree_options_for_select(projects, options = {})
160 def project_tree_options_for_select(projects, options = {})
161 s = ''
161 s = ''
162 project_tree(projects) do |project, level|
162 project_tree(projects) do |project, level|
163 name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
163 name_prefix = (level > 0 ? ('&nbsp;' * 2 * level + '&#187; ') : '')
164 tag_options = {:value => project.id, :selected => ((project == options[:selected]) ? 'selected' : nil)}
164 tag_options = {:value => project.id, :selected => ((project == options[:selected]) ? 'selected' : nil)}
165 tag_options.merge!(yield(project)) if block_given?
165 tag_options.merge!(yield(project)) if block_given?
166 s << content_tag('option', name_prefix + h(project), tag_options)
166 s << content_tag('option', name_prefix + h(project), tag_options)
167 end
167 end
168 s
168 s
169 end
169 end
170
170
171 # Yields the given block for each project with its level in the tree
171 # Yields the given block for each project with its level in the tree
172 def project_tree(projects, &block)
172 def project_tree(projects, &block)
173 ancestors = []
173 ancestors = []
174 projects.sort_by(&:lft).each do |project|
174 projects.sort_by(&:lft).each do |project|
175 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
175 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
176 ancestors.pop
176 ancestors.pop
177 end
177 end
178 yield project, ancestors.size
178 yield project, ancestors.size
179 ancestors << project
179 ancestors << project
180 end
180 end
181 end
181 end
182
182
183 def project_nested_ul(projects, &block)
183 def project_nested_ul(projects, &block)
184 s = ''
184 s = ''
185 if projects.any?
185 if projects.any?
186 ancestors = []
186 ancestors = []
187 projects.sort_by(&:lft).each do |project|
187 projects.sort_by(&:lft).each do |project|
188 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
188 if (ancestors.empty? || project.is_descendant_of?(ancestors.last))
189 s << "<ul>\n"
189 s << "<ul>\n"
190 else
190 else
191 ancestors.pop
191 ancestors.pop
192 s << "</li>"
192 s << "</li>"
193 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
193 while (ancestors.any? && !project.is_descendant_of?(ancestors.last))
194 ancestors.pop
194 ancestors.pop
195 s << "</ul></li>\n"
195 s << "</ul></li>\n"
196 end
196 end
197 end
197 end
198 s << "<li>"
198 s << "<li>"
199 s << yield(project).to_s
199 s << yield(project).to_s
200 ancestors << project
200 ancestors << project
201 end
201 end
202 s << ("</li></ul>\n" * ancestors.size)
202 s << ("</li></ul>\n" * ancestors.size)
203 end
203 end
204 s
204 s
205 end
205 end
206
206
207 def principals_check_box_tags(name, principals)
207 def principals_check_box_tags(name, principals)
208 s = ''
208 s = ''
209 principals.each do |principal|
209 principals.each do |principal|
210 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
210 s << "<label>#{ check_box_tag name, principal.id, false } #{h principal}</label>\n"
211 end
211 end
212 s
212 s
213 end
213 end
214
214
215 # Truncates and returns the string as a single line
215 # Truncates and returns the string as a single line
216 def truncate_single_line(string, *args)
216 def truncate_single_line(string, *args)
217 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
217 truncate(string.to_s, *args).gsub(%r{[\r\n]+}m, ' ')
218 end
218 end
219
219
220 def html_hours(text)
220 def html_hours(text)
221 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
221 text.gsub(%r{(\d+)\.(\d+)}, '<span class="hours hours-int">\1</span><span class="hours hours-dec">.\2</span>')
222 end
222 end
223
223
224 def authoring(created, author, options={})
224 def authoring(created, author, options={})
225 author_tag = (author.is_a?(User) && !author.anonymous?) ? link_to(h(author), :controller => 'account', :action => 'show', :id => author) : h(author || 'Anonymous')
225 l(options[:label] || :label_added_time_by, :author => link_to_user(author), :age => time_tag(created))
226 l(options[:label] || :label_added_time_by, :author => author_tag, :age => time_tag(created))
227 end
226 end
228
227
229 def time_tag(time)
228 def time_tag(time)
230 text = distance_of_time_in_words(Time.now, time)
229 text = distance_of_time_in_words(Time.now, time)
231 if @project
230 if @project
232 link_to(text, {:controller => 'projects', :action => 'activity', :id => @project, :from => time.to_date}, :title => format_time(time))
231 link_to(text, {:controller => 'projects', :action => 'activity', :id => @project, :from => time.to_date}, :title => format_time(time))
233 else
232 else
234 content_tag('acronym', text, :title => format_time(time))
233 content_tag('acronym', text, :title => format_time(time))
235 end
234 end
236 end
235 end
237
236
238 def syntax_highlight(name, content)
237 def syntax_highlight(name, content)
239 type = CodeRay::FileType[name]
238 type = CodeRay::FileType[name]
240 type ? CodeRay.scan(content, type).html : h(content)
239 type ? CodeRay.scan(content, type).html : h(content)
241 end
240 end
242
241
243 def to_path_param(path)
242 def to_path_param(path)
244 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
243 path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
245 end
244 end
246
245
247 def pagination_links_full(paginator, count=nil, options={})
246 def pagination_links_full(paginator, count=nil, options={})
248 page_param = options.delete(:page_param) || :page
247 page_param = options.delete(:page_param) || :page
249 url_param = params.dup
248 url_param = params.dup
250 # don't reuse query params if filters are present
249 # don't reuse query params if filters are present
251 url_param.merge!(:fields => nil, :values => nil, :operators => nil) if url_param.delete(:set_filter)
250 url_param.merge!(:fields => nil, :values => nil, :operators => nil) if url_param.delete(:set_filter)
252
251
253 html = ''
252 html = ''
254 if paginator.current.previous
253 if paginator.current.previous
255 html << link_to_remote_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
254 html << link_to_remote_content_update('&#171; ' + l(:label_previous), url_param.merge(page_param => paginator.current.previous)) + ' '
256 end
255 end
257
256
258 html << (pagination_links_each(paginator, options) do |n|
257 html << (pagination_links_each(paginator, options) do |n|
259 link_to_remote_content_update(n.to_s, url_param.merge(page_param => n))
258 link_to_remote_content_update(n.to_s, url_param.merge(page_param => n))
260 end || '')
259 end || '')
261
260
262 if paginator.current.next
261 if paginator.current.next
263 html << ' ' + link_to_remote_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
262 html << ' ' + link_to_remote_content_update((l(:label_next) + ' &#187;'), url_param.merge(page_param => paginator.current.next))
264 end
263 end
265
264
266 unless count.nil?
265 unless count.nil?
267 html << [
266 html << [
268 " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})",
267 " (#{paginator.current.first_item}-#{paginator.current.last_item}/#{count})",
269 per_page_links(paginator.items_per_page)
268 per_page_links(paginator.items_per_page)
270 ].compact.join(' | ')
269 ].compact.join(' | ')
271 end
270 end
272
271
273 html
272 html
274 end
273 end
275
274
276 def per_page_links(selected=nil)
275 def per_page_links(selected=nil)
277 url_param = params.dup
276 url_param = params.dup
278 url_param.clear if url_param.has_key?(:set_filter)
277 url_param.clear if url_param.has_key?(:set_filter)
279
278
280 links = Setting.per_page_options_array.collect do |n|
279 links = Setting.per_page_options_array.collect do |n|
281 n == selected ? n : link_to_remote(n, {:update => "content",
280 n == selected ? n : link_to_remote(n, {:update => "content",
282 :url => params.dup.merge(:per_page => n),
281 :url => params.dup.merge(:per_page => n),
283 :method => :get},
282 :method => :get},
284 {:href => url_for(url_param.merge(:per_page => n))})
283 {:href => url_for(url_param.merge(:per_page => n))})
285 end
284 end
286 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
285 links.size > 1 ? l(:label_display_per_page, links.join(', ')) : nil
287 end
286 end
288
287
289 def reorder_links(name, url)
288 def reorder_links(name, url)
290 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)), url.merge({"#{name}[move_to]" => 'highest'}), :method => :post, :title => l(:label_sort_highest)) +
289 link_to(image_tag('2uparrow.png', :alt => l(:label_sort_highest)), url.merge({"#{name}[move_to]" => 'highest'}), :method => :post, :title => l(:label_sort_highest)) +
291 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)), url.merge({"#{name}[move_to]" => 'higher'}), :method => :post, :title => l(:label_sort_higher)) +
290 link_to(image_tag('1uparrow.png', :alt => l(:label_sort_higher)), url.merge({"#{name}[move_to]" => 'higher'}), :method => :post, :title => l(:label_sort_higher)) +
292 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)), url.merge({"#{name}[move_to]" => 'lower'}), :method => :post, :title => l(:label_sort_lower)) +
291 link_to(image_tag('1downarrow.png', :alt => l(:label_sort_lower)), url.merge({"#{name}[move_to]" => 'lower'}), :method => :post, :title => l(:label_sort_lower)) +
293 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), url.merge({"#{name}[move_to]" => 'lowest'}), :method => :post, :title => l(:label_sort_lowest))
292 link_to(image_tag('2downarrow.png', :alt => l(:label_sort_lowest)), url.merge({"#{name}[move_to]" => 'lowest'}), :method => :post, :title => l(:label_sort_lowest))
294 end
293 end
295
294
296 def breadcrumb(*args)
295 def breadcrumb(*args)
297 elements = args.flatten
296 elements = args.flatten
298 elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
297 elements.any? ? content_tag('p', args.join(' &#187; ') + ' &#187; ', :class => 'breadcrumb') : nil
299 end
298 end
300
299
301 def other_formats_links(&block)
300 def other_formats_links(&block)
302 concat('<p class="other-formats">' + l(:label_export_to))
301 concat('<p class="other-formats">' + l(:label_export_to))
303 yield Redmine::Views::OtherFormatsBuilder.new(self)
302 yield Redmine::Views::OtherFormatsBuilder.new(self)
304 concat('</p>')
303 concat('</p>')
305 end
304 end
306
305
307 def page_header_title
306 def page_header_title
308 if @project.nil? || @project.new_record?
307 if @project.nil? || @project.new_record?
309 h(Setting.app_title)
308 h(Setting.app_title)
310 else
309 else
311 b = []
310 b = []
312 ancestors = (@project.root? ? [] : @project.ancestors.visible)
311 ancestors = (@project.root? ? [] : @project.ancestors.visible)
313 if ancestors.any?
312 if ancestors.any?
314 root = ancestors.shift
313 root = ancestors.shift
315 b << link_to(h(root), {:controller => 'projects', :action => 'show', :id => root, :jump => current_menu_item}, :class => 'root')
314 b << link_to(h(root), {:controller => 'projects', :action => 'show', :id => root, :jump => current_menu_item}, :class => 'root')
316 if ancestors.size > 2
315 if ancestors.size > 2
317 b << '&#8230;'
316 b << '&#8230;'
318 ancestors = ancestors[-2, 2]
317 ancestors = ancestors[-2, 2]
319 end
318 end
320 b += ancestors.collect {|p| link_to(h(p), {:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item}, :class => 'ancestor') }
319 b += ancestors.collect {|p| link_to(h(p), {:controller => 'projects', :action => 'show', :id => p, :jump => current_menu_item}, :class => 'ancestor') }
321 end
320 end
322 b << h(@project)
321 b << h(@project)
323 b.join(' &#187; ')
322 b.join(' &#187; ')
324 end
323 end
325 end
324 end
326
325
327 def html_title(*args)
326 def html_title(*args)
328 if args.empty?
327 if args.empty?
329 title = []
328 title = []
330 title << @project.name if @project
329 title << @project.name if @project
331 title += @html_title if @html_title
330 title += @html_title if @html_title
332 title << Setting.app_title
331 title << Setting.app_title
333 title.select {|t| !t.blank? }.join(' - ')
332 title.select {|t| !t.blank? }.join(' - ')
334 else
333 else
335 @html_title ||= []
334 @html_title ||= []
336 @html_title += args
335 @html_title += args
337 end
336 end
338 end
337 end
339
338
340 def accesskey(s)
339 def accesskey(s)
341 Redmine::AccessKeys.key_for s
340 Redmine::AccessKeys.key_for s
342 end
341 end
343
342
344 # Formats text according to system settings.
343 # Formats text according to system settings.
345 # 2 ways to call this method:
344 # 2 ways to call this method:
346 # * with a String: textilizable(text, options)
345 # * with a String: textilizable(text, options)
347 # * with an object and one of its attribute: textilizable(issue, :description, options)
346 # * with an object and one of its attribute: textilizable(issue, :description, options)
348 def textilizable(*args)
347 def textilizable(*args)
349 options = args.last.is_a?(Hash) ? args.pop : {}
348 options = args.last.is_a?(Hash) ? args.pop : {}
350 case args.size
349 case args.size
351 when 1
350 when 1
352 obj = options[:object]
351 obj = options[:object]
353 text = args.shift
352 text = args.shift
354 when 2
353 when 2
355 obj = args.shift
354 obj = args.shift
356 text = obj.send(args.shift).to_s
355 text = obj.send(args.shift).to_s
357 else
356 else
358 raise ArgumentError, 'invalid arguments to textilizable'
357 raise ArgumentError, 'invalid arguments to textilizable'
359 end
358 end
360 return '' if text.blank?
359 return '' if text.blank?
361
360
362 only_path = options.delete(:only_path) == false ? false : true
361 only_path = options.delete(:only_path) == false ? false : true
363
362
364 # when using an image link, try to use an attachment, if possible
363 # when using an image link, try to use an attachment, if possible
365 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
364 attachments = options[:attachments] || (obj && obj.respond_to?(:attachments) ? obj.attachments : nil)
366
365
367 if attachments
366 if attachments
368 attachments = attachments.sort_by(&:created_on).reverse
367 attachments = attachments.sort_by(&:created_on).reverse
369 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
368 text = text.gsub(/!((\<|\=|\>)?(\([^\)]+\))?(\[[^\]]+\])?(\{[^\}]+\})?)(\S+\.(bmp|gif|jpg|jpeg|png))!/i) do |m|
370 style = $1
369 style = $1
371 filename = $6.downcase
370 filename = $6.downcase
372 # search for the picture in attachments
371 # search for the picture in attachments
373 if found = attachments.detect { |att| att.filename.downcase == filename }
372 if found = attachments.detect { |att| att.filename.downcase == filename }
374 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
373 image_url = url_for :only_path => only_path, :controller => 'attachments', :action => 'download', :id => found
375 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
374 desc = found.description.to_s.gsub(/^([^\(\)]*).*$/, "\\1")
376 alt = desc.blank? ? nil : "(#{desc})"
375 alt = desc.blank? ? nil : "(#{desc})"
377 "!#{style}#{image_url}#{alt}!"
376 "!#{style}#{image_url}#{alt}!"
378 else
377 else
379 m
378 m
380 end
379 end
381 end
380 end
382 end
381 end
383
382
384 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) { |macro, args| exec_macro(macro, obj, args) }
383 text = Redmine::WikiFormatting.to_html(Setting.text_formatting, text) { |macro, args| exec_macro(macro, obj, args) }
385
384
386 # different methods for formatting wiki links
385 # different methods for formatting wiki links
387 case options[:wiki_links]
386 case options[:wiki_links]
388 when :local
387 when :local
389 # used for local links to html files
388 # used for local links to html files
390 format_wiki_link = Proc.new {|project, title, anchor| "#{title}.html" }
389 format_wiki_link = Proc.new {|project, title, anchor| "#{title}.html" }
391 when :anchor
390 when :anchor
392 # used for single-file wiki export
391 # used for single-file wiki export
393 format_wiki_link = Proc.new {|project, title, anchor| "##{title}" }
392 format_wiki_link = Proc.new {|project, title, anchor| "##{title}" }
394 else
393 else
395 format_wiki_link = Proc.new {|project, title, anchor| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title, :anchor => anchor) }
394 format_wiki_link = Proc.new {|project, title, anchor| url_for(:only_path => only_path, :controller => 'wiki', :action => 'index', :id => project, :page => title, :anchor => anchor) }
396 end
395 end
397
396
398 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
397 project = options[:project] || @project || (obj && obj.respond_to?(:project) ? obj.project : nil)
399
398
400 # Wiki links
399 # Wiki links
401 #
400 #
402 # Examples:
401 # Examples:
403 # [[mypage]]
402 # [[mypage]]
404 # [[mypage|mytext]]
403 # [[mypage|mytext]]
405 # wiki links can refer other project wikis, using project name or identifier:
404 # wiki links can refer other project wikis, using project name or identifier:
406 # [[project:]] -> wiki starting page
405 # [[project:]] -> wiki starting page
407 # [[project:|mytext]]
406 # [[project:|mytext]]
408 # [[project:mypage]]
407 # [[project:mypage]]
409 # [[project:mypage|mytext]]
408 # [[project:mypage|mytext]]
410 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
409 text = text.gsub(/(!)?(\[\[([^\]\n\|]+)(\|([^\]\n\|]+))?\]\])/) do |m|
411 link_project = project
410 link_project = project
412 esc, all, page, title = $1, $2, $3, $5
411 esc, all, page, title = $1, $2, $3, $5
413 if esc.nil?
412 if esc.nil?
414 if page =~ /^([^\:]+)\:(.*)$/
413 if page =~ /^([^\:]+)\:(.*)$/
415 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
414 link_project = Project.find_by_name($1) || Project.find_by_identifier($1)
416 page = $2
415 page = $2
417 title ||= $1 if page.blank?
416 title ||= $1 if page.blank?
418 end
417 end
419
418
420 if link_project && link_project.wiki
419 if link_project && link_project.wiki
421 # extract anchor
420 # extract anchor
422 anchor = nil
421 anchor = nil
423 if page =~ /^(.+?)\#(.+)$/
422 if page =~ /^(.+?)\#(.+)$/
424 page, anchor = $1, $2
423 page, anchor = $1, $2
425 end
424 end
426 # check if page exists
425 # check if page exists
427 wiki_page = link_project.wiki.find_page(page)
426 wiki_page = link_project.wiki.find_page(page)
428 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page), anchor),
427 link_to((title || page), format_wiki_link.call(link_project, Wiki.titleize(page), anchor),
429 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
428 :class => ('wiki-page' + (wiki_page ? '' : ' new')))
430 else
429 else
431 # project or wiki doesn't exist
430 # project or wiki doesn't exist
432 all
431 all
433 end
432 end
434 else
433 else
435 all
434 all
436 end
435 end
437 end
436 end
438
437
439 # Redmine links
438 # Redmine links
440 #
439 #
441 # Examples:
440 # Examples:
442 # Issues:
441 # Issues:
443 # #52 -> Link to issue #52
442 # #52 -> Link to issue #52
444 # Changesets:
443 # Changesets:
445 # r52 -> Link to revision 52
444 # r52 -> Link to revision 52
446 # commit:a85130f -> Link to scmid starting with a85130f
445 # commit:a85130f -> Link to scmid starting with a85130f
447 # Documents:
446 # Documents:
448 # document#17 -> Link to document with id 17
447 # document#17 -> Link to document with id 17
449 # document:Greetings -> Link to the document with title "Greetings"
448 # document:Greetings -> Link to the document with title "Greetings"
450 # document:"Some document" -> Link to the document with title "Some document"
449 # document:"Some document" -> Link to the document with title "Some document"
451 # Versions:
450 # Versions:
452 # version#3 -> Link to version with id 3
451 # version#3 -> Link to version with id 3
453 # version:1.0.0 -> Link to version named "1.0.0"
452 # version:1.0.0 -> Link to version named "1.0.0"
454 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
453 # version:"1.0 beta 2" -> Link to version named "1.0 beta 2"
455 # Attachments:
454 # Attachments:
456 # attachment:file.zip -> Link to the attachment of the current object named file.zip
455 # attachment:file.zip -> Link to the attachment of the current object named file.zip
457 # Source files:
456 # Source files:
458 # source:some/file -> Link to the file located at /some/file in the project's repository
457 # source:some/file -> Link to the file located at /some/file in the project's repository
459 # source:some/file@52 -> Link to the file's revision 52
458 # source:some/file@52 -> Link to the file's revision 52
460 # source:some/file#L120 -> Link to line 120 of the file
459 # source:some/file#L120 -> Link to line 120 of the file
461 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
460 # source:some/file@52#L120 -> Link to line 120 of the file's revision 52
462 # export:some/file -> Force the download of the file
461 # export:some/file -> Force the download of the file
463 # Forum messages:
462 # Forum messages:
464 # message#1218 -> Link to message with id 1218
463 # message#1218 -> Link to message with id 1218
465 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|<|$)}) do |m|
464 text = text.gsub(%r{([\s\(,\-\>]|^)(!)?(attachment|document|version|commit|source|export|message)?((#|r)(\d+)|(:)([^"\s<>][^\s<>]*?|"[^"]+?"))(?=(?=[[:punct:]]\W)|,|\s|<|$)}) do |m|
466 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
465 leading, esc, prefix, sep, oid = $1, $2, $3, $5 || $7, $6 || $8
467 link = nil
466 link = nil
468 if esc.nil?
467 if esc.nil?
469 if prefix.nil? && sep == 'r'
468 if prefix.nil? && sep == 'r'
470 if project && (changeset = project.changesets.find_by_revision(oid))
469 if project && (changeset = project.changesets.find_by_revision(oid))
471 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
470 link = link_to("r#{oid}", {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => oid},
472 :class => 'changeset',
471 :class => 'changeset',
473 :title => truncate_single_line(changeset.comments, :length => 100))
472 :title => truncate_single_line(changeset.comments, :length => 100))
474 end
473 end
475 elsif sep == '#'
474 elsif sep == '#'
476 oid = oid.to_i
475 oid = oid.to_i
477 case prefix
476 case prefix
478 when nil
477 when nil
479 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
478 if issue = Issue.find_by_id(oid, :include => [:project, :status], :conditions => Project.visible_by(User.current))
480 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
479 link = link_to("##{oid}", {:only_path => only_path, :controller => 'issues', :action => 'show', :id => oid},
481 :class => (issue.closed? ? 'issue closed' : 'issue'),
480 :class => (issue.closed? ? 'issue closed' : 'issue'),
482 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
481 :title => "#{truncate(issue.subject, :length => 100)} (#{issue.status.name})")
483 link = content_tag('del', link) if issue.closed?
482 link = content_tag('del', link) if issue.closed?
484 end
483 end
485 when 'document'
484 when 'document'
486 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
485 if document = Document.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
487 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
486 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
488 :class => 'document'
487 :class => 'document'
489 end
488 end
490 when 'version'
489 when 'version'
491 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
490 if version = Version.find_by_id(oid, :include => [:project], :conditions => Project.visible_by(User.current))
492 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
491 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
493 :class => 'version'
492 :class => 'version'
494 end
493 end
495 when 'message'
494 when 'message'
496 if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
495 if message = Message.find_by_id(oid, :include => [:parent, {:board => :project}], :conditions => Project.visible_by(User.current))
497 link = link_to h(truncate(message.subject, :length => 60)), {:only_path => only_path,
496 link = link_to h(truncate(message.subject, :length => 60)), {:only_path => only_path,
498 :controller => 'messages',
497 :controller => 'messages',
499 :action => 'show',
498 :action => 'show',
500 :board_id => message.board,
499 :board_id => message.board,
501 :id => message.root,
500 :id => message.root,
502 :anchor => (message.parent ? "message-#{message.id}" : nil)},
501 :anchor => (message.parent ? "message-#{message.id}" : nil)},
503 :class => 'message'
502 :class => 'message'
504 end
503 end
505 end
504 end
506 elsif sep == ':'
505 elsif sep == ':'
507 # removes the double quotes if any
506 # removes the double quotes if any
508 name = oid.gsub(%r{^"(.*)"$}, "\\1")
507 name = oid.gsub(%r{^"(.*)"$}, "\\1")
509 case prefix
508 case prefix
510 when 'document'
509 when 'document'
511 if project && document = project.documents.find_by_title(name)
510 if project && document = project.documents.find_by_title(name)
512 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
511 link = link_to h(document.title), {:only_path => only_path, :controller => 'documents', :action => 'show', :id => document},
513 :class => 'document'
512 :class => 'document'
514 end
513 end
515 when 'version'
514 when 'version'
516 if project && version = project.versions.find_by_name(name)
515 if project && version = project.versions.find_by_name(name)
517 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
516 link = link_to h(version.name), {:only_path => only_path, :controller => 'versions', :action => 'show', :id => version},
518 :class => 'version'
517 :class => 'version'
519 end
518 end
520 when 'commit'
519 when 'commit'
521 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
520 if project && (changeset = project.changesets.find(:first, :conditions => ["scmid LIKE ?", "#{name}%"]))
522 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
521 link = link_to h("#{name}"), {:only_path => only_path, :controller => 'repositories', :action => 'revision', :id => project, :rev => changeset.revision},
523 :class => 'changeset',
522 :class => 'changeset',
524 :title => truncate_single_line(changeset.comments, :length => 100)
523 :title => truncate_single_line(changeset.comments, :length => 100)
525 end
524 end
526 when 'source', 'export'
525 when 'source', 'export'
527 if project && project.repository
526 if project && project.repository
528 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
527 name =~ %r{^[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?$}
529 path, rev, anchor = $1, $3, $5
528 path, rev, anchor = $1, $3, $5
530 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
529 link = link_to h("#{prefix}:#{name}"), {:controller => 'repositories', :action => 'entry', :id => project,
531 :path => to_path_param(path),
530 :path => to_path_param(path),
532 :rev => rev,
531 :rev => rev,
533 :anchor => anchor,
532 :anchor => anchor,
534 :format => (prefix == 'export' ? 'raw' : nil)},
533 :format => (prefix == 'export' ? 'raw' : nil)},
535 :class => (prefix == 'export' ? 'source download' : 'source')
534 :class => (prefix == 'export' ? 'source download' : 'source')
536 end
535 end
537 when 'attachment'
536 when 'attachment'
538 if attachments && attachment = attachments.detect {|a| a.filename == name }
537 if attachments && attachment = attachments.detect {|a| a.filename == name }
539 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
538 link = link_to h(attachment.filename), {:only_path => only_path, :controller => 'attachments', :action => 'download', :id => attachment},
540 :class => 'attachment'
539 :class => 'attachment'
541 end
540 end
542 end
541 end
543 end
542 end
544 end
543 end
545 leading + (link || "#{prefix}#{sep}#{oid}")
544 leading + (link || "#{prefix}#{sep}#{oid}")
546 end
545 end
547
546
548 text
547 text
549 end
548 end
550
549
551 # Same as Rails' simple_format helper without using paragraphs
550 # Same as Rails' simple_format helper without using paragraphs
552 def simple_format_without_paragraph(text)
551 def simple_format_without_paragraph(text)
553 text.to_s.
552 text.to_s.
554 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
553 gsub(/\r\n?/, "\n"). # \r\n and \r -> \n
555 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
554 gsub(/\n\n+/, "<br /><br />"). # 2+ newline -> 2 br
556 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
555 gsub(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br
557 end
556 end
558
557
559 def lang_options_for_select(blank=true)
558 def lang_options_for_select(blank=true)
560 (blank ? [["(auto)", ""]] : []) +
559 (blank ? [["(auto)", ""]] : []) +
561 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
560 valid_languages.collect{|lang| [ ll(lang.to_s, :general_lang_name), lang.to_s]}.sort{|x,y| x.last <=> y.last }
562 end
561 end
563
562
564 def label_tag_for(name, option_tags = nil, options = {})
563 def label_tag_for(name, option_tags = nil, options = {})
565 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
564 label_text = l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) + (options.delete(:required) ? @template.content_tag("span", " *", :class => "required"): "")
566 content_tag("label", label_text)
565 content_tag("label", label_text)
567 end
566 end
568
567
569 def labelled_tabular_form_for(name, object, options, &proc)
568 def labelled_tabular_form_for(name, object, options, &proc)
570 options[:html] ||= {}
569 options[:html] ||= {}
571 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
570 options[:html][:class] = 'tabular' unless options[:html].has_key?(:class)
572 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
571 form_for(name, object, options.merge({ :builder => TabularFormBuilder, :lang => current_language}), &proc)
573 end
572 end
574
573
575 def back_url_hidden_field_tag
574 def back_url_hidden_field_tag
576 back_url = params[:back_url] || request.env['HTTP_REFERER']
575 back_url = params[:back_url] || request.env['HTTP_REFERER']
577 back_url = CGI.unescape(back_url.to_s)
576 back_url = CGI.unescape(back_url.to_s)
578 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
577 hidden_field_tag('back_url', CGI.escape(back_url)) unless back_url.blank?
579 end
578 end
580
579
581 def check_all_links(form_name)
580 def check_all_links(form_name)
582 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
581 link_to_function(l(:button_check_all), "checkAll('#{form_name}', true)") +
583 " | " +
582 " | " +
584 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
583 link_to_function(l(:button_uncheck_all), "checkAll('#{form_name}', false)")
585 end
584 end
586
585
587 def progress_bar(pcts, options={})
586 def progress_bar(pcts, options={})
588 pcts = [pcts, pcts] unless pcts.is_a?(Array)
587 pcts = [pcts, pcts] unless pcts.is_a?(Array)
589 pcts[1] = pcts[1] - pcts[0]
588 pcts[1] = pcts[1] - pcts[0]
590 pcts << (100 - pcts[1] - pcts[0])
589 pcts << (100 - pcts[1] - pcts[0])
591 width = options[:width] || '100px;'
590 width = options[:width] || '100px;'
592 legend = options[:legend] || ''
591 legend = options[:legend] || ''
593 content_tag('table',
592 content_tag('table',
594 content_tag('tr',
593 content_tag('tr',
595 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0].floor}%;", :class => 'closed') : '') +
594 (pcts[0] > 0 ? content_tag('td', '', :style => "width: #{pcts[0].floor}%;", :class => 'closed') : '') +
596 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1].floor}%;", :class => 'done') : '') +
595 (pcts[1] > 0 ? content_tag('td', '', :style => "width: #{pcts[1].floor}%;", :class => 'done') : '') +
597 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2].floor}%;", :class => 'todo') : '')
596 (pcts[2] > 0 ? content_tag('td', '', :style => "width: #{pcts[2].floor}%;", :class => 'todo') : '')
598 ), :class => 'progress', :style => "width: #{width};") +
597 ), :class => 'progress', :style => "width: #{width};") +
599 content_tag('p', legend, :class => 'pourcent')
598 content_tag('p', legend, :class => 'pourcent')
600 end
599 end
601
600
602 def context_menu_link(name, url, options={})
601 def context_menu_link(name, url, options={})
603 options[:class] ||= ''
602 options[:class] ||= ''
604 if options.delete(:selected)
603 if options.delete(:selected)
605 options[:class] << ' icon-checked disabled'
604 options[:class] << ' icon-checked disabled'
606 options[:disabled] = true
605 options[:disabled] = true
607 end
606 end
608 if options.delete(:disabled)
607 if options.delete(:disabled)
609 options.delete(:method)
608 options.delete(:method)
610 options.delete(:confirm)
609 options.delete(:confirm)
611 options.delete(:onclick)
610 options.delete(:onclick)
612 options[:class] << ' disabled'
611 options[:class] << ' disabled'
613 url = '#'
612 url = '#'
614 end
613 end
615 link_to name, url, options
614 link_to name, url, options
616 end
615 end
617
616
618 def calendar_for(field_id)
617 def calendar_for(field_id)
619 include_calendar_headers_tags
618 include_calendar_headers_tags
620 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
619 image_tag("calendar.png", {:id => "#{field_id}_trigger",:class => "calendar-trigger"}) +
621 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
620 javascript_tag("Calendar.setup({inputField : '#{field_id}', ifFormat : '%Y-%m-%d', button : '#{field_id}_trigger' });")
622 end
621 end
623
622
624 def include_calendar_headers_tags
623 def include_calendar_headers_tags
625 unless @calendar_headers_tags_included
624 unless @calendar_headers_tags_included
626 @calendar_headers_tags_included = true
625 @calendar_headers_tags_included = true
627 content_for :header_tags do
626 content_for :header_tags do
628 javascript_include_tag('calendar/calendar') +
627 javascript_include_tag('calendar/calendar') +
629 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
628 javascript_include_tag("calendar/lang/calendar-#{current_language.to_s.downcase}.js") +
630 javascript_include_tag('calendar/calendar-setup') +
629 javascript_include_tag('calendar/calendar-setup') +
631 stylesheet_link_tag('calendar')
630 stylesheet_link_tag('calendar')
632 end
631 end
633 end
632 end
634 end
633 end
635
634
636 def content_for(name, content = nil, &block)
635 def content_for(name, content = nil, &block)
637 @has_content ||= {}
636 @has_content ||= {}
638 @has_content[name] = true
637 @has_content[name] = true
639 super(name, content, &block)
638 super(name, content, &block)
640 end
639 end
641
640
642 def has_content?(name)
641 def has_content?(name)
643 (@has_content && @has_content[name]) || false
642 (@has_content && @has_content[name]) || false
644 end
643 end
645
644
646 # Returns the avatar image tag for the given +user+ if avatars are enabled
645 # Returns the avatar image tag for the given +user+ if avatars are enabled
647 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
646 # +user+ can be a User or a string that will be scanned for an email address (eg. 'joe <joe@foo.bar>')
648 def avatar(user, options = { })
647 def avatar(user, options = { })
649 if Setting.gravatar_enabled?
648 if Setting.gravatar_enabled?
650 options.merge!({:ssl => Setting.protocol == 'https'})
649 options.merge!({:ssl => Setting.protocol == 'https'})
651 email = nil
650 email = nil
652 if user.respond_to?(:mail)
651 if user.respond_to?(:mail)
653 email = user.mail
652 email = user.mail
654 elsif user.to_s =~ %r{<(.+?)>}
653 elsif user.to_s =~ %r{<(.+?)>}
655 email = $1
654 email = $1
656 end
655 end
657 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
656 return gravatar(email.to_s.downcase, options) unless email.blank? rescue nil
658 end
657 end
659 end
658 end
660
659
661 private
660 private
662
661
663 def wiki_helper
662 def wiki_helper
664 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
663 helper = Redmine::WikiFormatting.helper_for(Setting.text_formatting)
665 extend helper
664 extend helper
666 return self
665 return self
667 end
666 end
668
667
669 def link_to_remote_content_update(text, url_params)
668 def link_to_remote_content_update(text, url_params)
670 link_to_remote(text,
669 link_to_remote(text,
671 {:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
670 {:url => url_params, :method => :get, :update => 'content', :complete => 'window.scrollTo(0,0)'},
672 {:href => url_for(:params => url_params)}
671 {:href => url_for(:params => url_params)}
673 )
672 )
674 end
673 end
675
674
676 end
675 end
1 NO CONTENT: file renamed from app/views/account/show.rhtml to app/views/users/show.rhtml
NO CONTENT: file renamed from app/views/account/show.rhtml to app/views/users/show.rhtml
@@ -1,262 +1,263
1 ActionController::Routing::Routes.draw do |map|
1 ActionController::Routing::Routes.draw do |map|
2 # Add your own custom routes here.
2 # Add your own custom routes here.
3 # The priority is based upon order of creation: first created -> highest priority.
3 # The priority is based upon order of creation: first created -> highest priority.
4
4
5 # Here's a sample route:
5 # Here's a sample route:
6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
7 # Keep in mind you can assign values other than :controller and :action
7 # Keep in mind you can assign values other than :controller and :action
8
8
9 map.home '', :controller => 'welcome'
9 map.home '', :controller => 'welcome'
10
10
11 map.signin 'login', :controller => 'account', :action => 'login'
11 map.signin 'login', :controller => 'account', :action => 'login'
12 map.signout 'logout', :controller => 'account', :action => 'logout'
12 map.signout 'logout', :controller => 'account', :action => 'logout'
13
13
14 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
14 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
15 map.connect 'help/:ctrl/:page', :controller => 'help'
15 map.connect 'help/:ctrl/:page', :controller => 'help'
16
16
17 map.connect 'time_entries/:id/edit', :action => 'edit', :controller => 'timelog'
17 map.connect 'time_entries/:id/edit', :action => 'edit', :controller => 'timelog'
18 map.connect 'projects/:project_id/time_entries/new', :action => 'edit', :controller => 'timelog'
18 map.connect 'projects/:project_id/time_entries/new', :action => 'edit', :controller => 'timelog'
19 map.connect 'projects/:project_id/issues/:issue_id/time_entries/new', :action => 'edit', :controller => 'timelog'
19 map.connect 'projects/:project_id/issues/:issue_id/time_entries/new', :action => 'edit', :controller => 'timelog'
20
20
21 map.with_options :controller => 'timelog' do |timelog|
21 map.with_options :controller => 'timelog' do |timelog|
22 timelog.connect 'projects/:project_id/time_entries', :action => 'details'
22 timelog.connect 'projects/:project_id/time_entries', :action => 'details'
23
23
24 timelog.with_options :action => 'details', :conditions => {:method => :get} do |time_details|
24 timelog.with_options :action => 'details', :conditions => {:method => :get} do |time_details|
25 time_details.connect 'time_entries'
25 time_details.connect 'time_entries'
26 time_details.connect 'time_entries.:format'
26 time_details.connect 'time_entries.:format'
27 time_details.connect 'issues/:issue_id/time_entries'
27 time_details.connect 'issues/:issue_id/time_entries'
28 time_details.connect 'issues/:issue_id/time_entries.:format'
28 time_details.connect 'issues/:issue_id/time_entries.:format'
29 time_details.connect 'projects/:project_id/time_entries.:format'
29 time_details.connect 'projects/:project_id/time_entries.:format'
30 time_details.connect 'projects/:project_id/issues/:issue_id/time_entries'
30 time_details.connect 'projects/:project_id/issues/:issue_id/time_entries'
31 time_details.connect 'projects/:project_id/issues/:issue_id/time_entries.:format'
31 time_details.connect 'projects/:project_id/issues/:issue_id/time_entries.:format'
32 end
32 end
33 timelog.connect 'projects/:project_id/time_entries/report', :action => 'report'
33 timelog.connect 'projects/:project_id/time_entries/report', :action => 'report'
34 timelog.with_options :action => 'report',:conditions => {:method => :get} do |time_report|
34 timelog.with_options :action => 'report',:conditions => {:method => :get} do |time_report|
35 time_report.connect 'time_entries/report'
35 time_report.connect 'time_entries/report'
36 time_report.connect 'time_entries/report.:format'
36 time_report.connect 'time_entries/report.:format'
37 time_report.connect 'projects/:project_id/time_entries/report.:format'
37 time_report.connect 'projects/:project_id/time_entries/report.:format'
38 end
38 end
39
39
40 timelog.with_options :action => 'edit', :conditions => {:method => :get} do |time_edit|
40 timelog.with_options :action => 'edit', :conditions => {:method => :get} do |time_edit|
41 time_edit.connect 'issues/:issue_id/time_entries/new'
41 time_edit.connect 'issues/:issue_id/time_entries/new'
42 end
42 end
43
43
44 timelog.connect 'time_entries/:id/destroy', :action => 'destroy', :conditions => {:method => :post}
44 timelog.connect 'time_entries/:id/destroy', :action => 'destroy', :conditions => {:method => :post}
45 end
45 end
46
46
47 map.connect 'projects/:id/wiki', :controller => 'wikis', :action => 'edit', :conditions => {:method => :post}
47 map.connect 'projects/:id/wiki', :controller => 'wikis', :action => 'edit', :conditions => {:method => :post}
48 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :get}
48 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :get}
49 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :post}
49 map.connect 'projects/:id/wiki/destroy', :controller => 'wikis', :action => 'destroy', :conditions => {:method => :post}
50 map.with_options :controller => 'wiki' do |wiki_routes|
50 map.with_options :controller => 'wiki' do |wiki_routes|
51 wiki_routes.with_options :conditions => {:method => :get} do |wiki_views|
51 wiki_routes.with_options :conditions => {:method => :get} do |wiki_views|
52 wiki_views.connect 'projects/:id/wiki/:page', :action => 'special', :page => /page_index|date_index|export/i
52 wiki_views.connect 'projects/:id/wiki/:page', :action => 'special', :page => /page_index|date_index|export/i
53 wiki_views.connect 'projects/:id/wiki/:page', :action => 'index', :page => nil
53 wiki_views.connect 'projects/:id/wiki/:page', :action => 'index', :page => nil
54 wiki_views.connect 'projects/:id/wiki/:page/edit', :action => 'edit'
54 wiki_views.connect 'projects/:id/wiki/:page/edit', :action => 'edit'
55 wiki_views.connect 'projects/:id/wiki/:page/rename', :action => 'rename'
55 wiki_views.connect 'projects/:id/wiki/:page/rename', :action => 'rename'
56 wiki_views.connect 'projects/:id/wiki/:page/history', :action => 'history'
56 wiki_views.connect 'projects/:id/wiki/:page/history', :action => 'history'
57 wiki_views.connect 'projects/:id/wiki/:page/diff/:version/vs/:version_from', :action => 'diff'
57 wiki_views.connect 'projects/:id/wiki/:page/diff/:version/vs/:version_from', :action => 'diff'
58 wiki_views.connect 'projects/:id/wiki/:page/annotate/:version', :action => 'annotate'
58 wiki_views.connect 'projects/:id/wiki/:page/annotate/:version', :action => 'annotate'
59 end
59 end
60
60
61 wiki_routes.connect 'projects/:id/wiki/:page/:action',
61 wiki_routes.connect 'projects/:id/wiki/:page/:action',
62 :action => /edit|rename|destroy|preview|protect/,
62 :action => /edit|rename|destroy|preview|protect/,
63 :conditions => {:method => :post}
63 :conditions => {:method => :post}
64 end
64 end
65
65
66 map.with_options :controller => 'messages' do |messages_routes|
66 map.with_options :controller => 'messages' do |messages_routes|
67 messages_routes.with_options :conditions => {:method => :get} do |messages_views|
67 messages_routes.with_options :conditions => {:method => :get} do |messages_views|
68 messages_views.connect 'boards/:board_id/topics/new', :action => 'new'
68 messages_views.connect 'boards/:board_id/topics/new', :action => 'new'
69 messages_views.connect 'boards/:board_id/topics/:id', :action => 'show'
69 messages_views.connect 'boards/:board_id/topics/:id', :action => 'show'
70 messages_views.connect 'boards/:board_id/topics/:id/edit', :action => 'edit'
70 messages_views.connect 'boards/:board_id/topics/:id/edit', :action => 'edit'
71 end
71 end
72 messages_routes.with_options :conditions => {:method => :post} do |messages_actions|
72 messages_routes.with_options :conditions => {:method => :post} do |messages_actions|
73 messages_actions.connect 'boards/:board_id/topics/new', :action => 'new'
73 messages_actions.connect 'boards/:board_id/topics/new', :action => 'new'
74 messages_actions.connect 'boards/:board_id/topics/:id/replies', :action => 'reply'
74 messages_actions.connect 'boards/:board_id/topics/:id/replies', :action => 'reply'
75 messages_actions.connect 'boards/:board_id/topics/:id/:action', :action => /edit|destroy/
75 messages_actions.connect 'boards/:board_id/topics/:id/:action', :action => /edit|destroy/
76 end
76 end
77 end
77 end
78
78
79 map.with_options :controller => 'boards' do |board_routes|
79 map.with_options :controller => 'boards' do |board_routes|
80 board_routes.with_options :conditions => {:method => :get} do |board_views|
80 board_routes.with_options :conditions => {:method => :get} do |board_views|
81 board_views.connect 'projects/:project_id/boards', :action => 'index'
81 board_views.connect 'projects/:project_id/boards', :action => 'index'
82 board_views.connect 'projects/:project_id/boards/new', :action => 'new'
82 board_views.connect 'projects/:project_id/boards/new', :action => 'new'
83 board_views.connect 'projects/:project_id/boards/:id', :action => 'show'
83 board_views.connect 'projects/:project_id/boards/:id', :action => 'show'
84 board_views.connect 'projects/:project_id/boards/:id.:format', :action => 'show'
84 board_views.connect 'projects/:project_id/boards/:id.:format', :action => 'show'
85 board_views.connect 'projects/:project_id/boards/:id/edit', :action => 'edit'
85 board_views.connect 'projects/:project_id/boards/:id/edit', :action => 'edit'
86 end
86 end
87 board_routes.with_options :conditions => {:method => :post} do |board_actions|
87 board_routes.with_options :conditions => {:method => :post} do |board_actions|
88 board_actions.connect 'projects/:project_id/boards', :action => 'new'
88 board_actions.connect 'projects/:project_id/boards', :action => 'new'
89 board_actions.connect 'projects/:project_id/boards/:id/:action', :action => /edit|destroy/
89 board_actions.connect 'projects/:project_id/boards/:id/:action', :action => /edit|destroy/
90 end
90 end
91 end
91 end
92
92
93 map.with_options :controller => 'documents' do |document_routes|
93 map.with_options :controller => 'documents' do |document_routes|
94 document_routes.with_options :conditions => {:method => :get} do |document_views|
94 document_routes.with_options :conditions => {:method => :get} do |document_views|
95 document_views.connect 'projects/:project_id/documents', :action => 'index'
95 document_views.connect 'projects/:project_id/documents', :action => 'index'
96 document_views.connect 'projects/:project_id/documents/new', :action => 'new'
96 document_views.connect 'projects/:project_id/documents/new', :action => 'new'
97 document_views.connect 'documents/:id', :action => 'show'
97 document_views.connect 'documents/:id', :action => 'show'
98 document_views.connect 'documents/:id/edit', :action => 'edit'
98 document_views.connect 'documents/:id/edit', :action => 'edit'
99 end
99 end
100 document_routes.with_options :conditions => {:method => :post} do |document_actions|
100 document_routes.with_options :conditions => {:method => :post} do |document_actions|
101 document_actions.connect 'projects/:project_id/documents', :action => 'new'
101 document_actions.connect 'projects/:project_id/documents', :action => 'new'
102 document_actions.connect 'documents/:id/:action', :action => /destroy|edit/
102 document_actions.connect 'documents/:id/:action', :action => /destroy|edit/
103 end
103 end
104 end
104 end
105
105
106 map.with_options :controller => 'issues' do |issues_routes|
106 map.with_options :controller => 'issues' do |issues_routes|
107 issues_routes.with_options :conditions => {:method => :get} do |issues_views|
107 issues_routes.with_options :conditions => {:method => :get} do |issues_views|
108 issues_views.connect 'issues', :action => 'index'
108 issues_views.connect 'issues', :action => 'index'
109 issues_views.connect 'issues.:format', :action => 'index'
109 issues_views.connect 'issues.:format', :action => 'index'
110 issues_views.connect 'projects/:project_id/issues', :action => 'index'
110 issues_views.connect 'projects/:project_id/issues', :action => 'index'
111 issues_views.connect 'projects/:project_id/issues.:format', :action => 'index'
111 issues_views.connect 'projects/:project_id/issues.:format', :action => 'index'
112 issues_views.connect 'projects/:project_id/issues/new', :action => 'new'
112 issues_views.connect 'projects/:project_id/issues/new', :action => 'new'
113 issues_views.connect 'projects/:project_id/issues/gantt', :action => 'gantt'
113 issues_views.connect 'projects/:project_id/issues/gantt', :action => 'gantt'
114 issues_views.connect 'projects/:project_id/issues/calendar', :action => 'calendar'
114 issues_views.connect 'projects/:project_id/issues/calendar', :action => 'calendar'
115 issues_views.connect 'projects/:project_id/issues/:copy_from/copy', :action => 'new'
115 issues_views.connect 'projects/:project_id/issues/:copy_from/copy', :action => 'new'
116 issues_views.connect 'issues/:id', :action => 'show', :id => /\d+/
116 issues_views.connect 'issues/:id', :action => 'show', :id => /\d+/
117 issues_views.connect 'issues/:id.:format', :action => 'show', :id => /\d+/
117 issues_views.connect 'issues/:id.:format', :action => 'show', :id => /\d+/
118 issues_views.connect 'issues/:id/edit', :action => 'edit', :id => /\d+/
118 issues_views.connect 'issues/:id/edit', :action => 'edit', :id => /\d+/
119 issues_views.connect 'issues/:id/move', :action => 'move', :id => /\d+/
119 issues_views.connect 'issues/:id/move', :action => 'move', :id => /\d+/
120 end
120 end
121 issues_routes.with_options :conditions => {:method => :post} do |issues_actions|
121 issues_routes.with_options :conditions => {:method => :post} do |issues_actions|
122 issues_actions.connect 'projects/:project_id/issues', :action => 'new'
122 issues_actions.connect 'projects/:project_id/issues', :action => 'new'
123 issues_actions.connect 'issues/:id/quoted', :action => 'reply', :id => /\d+/
123 issues_actions.connect 'issues/:id/quoted', :action => 'reply', :id => /\d+/
124 issues_actions.connect 'issues/:id/:action', :action => /edit|move|destroy/, :id => /\d+/
124 issues_actions.connect 'issues/:id/:action', :action => /edit|move|destroy/, :id => /\d+/
125 end
125 end
126 issues_routes.connect 'issues/:action'
126 issues_routes.connect 'issues/:action'
127 end
127 end
128
128
129 map.with_options :controller => 'issue_relations', :conditions => {:method => :post} do |relations|
129 map.with_options :controller => 'issue_relations', :conditions => {:method => :post} do |relations|
130 relations.connect 'issues/:issue_id/relations/:id', :action => 'new'
130 relations.connect 'issues/:issue_id/relations/:id', :action => 'new'
131 relations.connect 'issues/:issue_id/relations/:id/destroy', :action => 'destroy'
131 relations.connect 'issues/:issue_id/relations/:id/destroy', :action => 'destroy'
132 end
132 end
133
133
134 map.with_options :controller => 'reports', :action => 'issue_report', :conditions => {:method => :get} do |reports|
134 map.with_options :controller => 'reports', :action => 'issue_report', :conditions => {:method => :get} do |reports|
135 reports.connect 'projects/:id/issues/report'
135 reports.connect 'projects/:id/issues/report'
136 reports.connect 'projects/:id/issues/report/:detail'
136 reports.connect 'projects/:id/issues/report/:detail'
137 end
137 end
138
138
139 map.with_options :controller => 'news' do |news_routes|
139 map.with_options :controller => 'news' do |news_routes|
140 news_routes.with_options :conditions => {:method => :get} do |news_views|
140 news_routes.with_options :conditions => {:method => :get} do |news_views|
141 news_views.connect 'news', :action => 'index'
141 news_views.connect 'news', :action => 'index'
142 news_views.connect 'projects/:project_id/news', :action => 'index'
142 news_views.connect 'projects/:project_id/news', :action => 'index'
143 news_views.connect 'projects/:project_id/news.:format', :action => 'index'
143 news_views.connect 'projects/:project_id/news.:format', :action => 'index'
144 news_views.connect 'news.:format', :action => 'index'
144 news_views.connect 'news.:format', :action => 'index'
145 news_views.connect 'projects/:project_id/news/new', :action => 'new'
145 news_views.connect 'projects/:project_id/news/new', :action => 'new'
146 news_views.connect 'news/:id', :action => 'show'
146 news_views.connect 'news/:id', :action => 'show'
147 news_views.connect 'news/:id/edit', :action => 'edit'
147 news_views.connect 'news/:id/edit', :action => 'edit'
148 end
148 end
149 news_routes.with_options do |news_actions|
149 news_routes.with_options do |news_actions|
150 news_actions.connect 'projects/:project_id/news', :action => 'new'
150 news_actions.connect 'projects/:project_id/news', :action => 'new'
151 news_actions.connect 'news/:id/edit', :action => 'edit'
151 news_actions.connect 'news/:id/edit', :action => 'edit'
152 news_actions.connect 'news/:id/destroy', :action => 'destroy'
152 news_actions.connect 'news/:id/destroy', :action => 'destroy'
153 end
153 end
154 end
154 end
155
155
156 map.connect 'projects/:id/members/new', :controller => 'members', :action => 'new'
156 map.connect 'projects/:id/members/new', :controller => 'members', :action => 'new'
157
157
158 map.with_options :controller => 'users' do |users|
158 map.with_options :controller => 'users' do |users|
159 users.with_options :conditions => {:method => :get} do |user_views|
159 users.with_options :conditions => {:method => :get} do |user_views|
160 user_views.connect 'users', :action => 'list'
160 user_views.connect 'users', :action => 'list'
161 user_views.connect 'users', :action => 'index'
161 user_views.connect 'users', :action => 'index'
162 user_views.connect 'users/:id', :action => 'show', :id => /\d+/
162 user_views.connect 'users/new', :action => 'add'
163 user_views.connect 'users/new', :action => 'add'
163 user_views.connect 'users/:id/edit/:tab', :action => 'edit', :tab => nil
164 user_views.connect 'users/:id/edit/:tab', :action => 'edit', :tab => nil
164 end
165 end
165 users.with_options :conditions => {:method => :post} do |user_actions|
166 users.with_options :conditions => {:method => :post} do |user_actions|
166 user_actions.connect 'users', :action => 'add'
167 user_actions.connect 'users', :action => 'add'
167 user_actions.connect 'users/new', :action => 'add'
168 user_actions.connect 'users/new', :action => 'add'
168 user_actions.connect 'users/:id/edit', :action => 'edit'
169 user_actions.connect 'users/:id/edit', :action => 'edit'
169 user_actions.connect 'users/:id/memberships', :action => 'edit_membership'
170 user_actions.connect 'users/:id/memberships', :action => 'edit_membership'
170 user_actions.connect 'users/:id/memberships/:membership_id', :action => 'edit_membership'
171 user_actions.connect 'users/:id/memberships/:membership_id', :action => 'edit_membership'
171 user_actions.connect 'users/:id/memberships/:membership_id/destroy', :action => 'destroy_membership'
172 user_actions.connect 'users/:id/memberships/:membership_id/destroy', :action => 'destroy_membership'
172 end
173 end
173 end
174 end
174
175
175 map.with_options :controller => 'projects' do |projects|
176 map.with_options :controller => 'projects' do |projects|
176 projects.with_options :conditions => {:method => :get} do |project_views|
177 projects.with_options :conditions => {:method => :get} do |project_views|
177 project_views.connect 'projects', :action => 'index'
178 project_views.connect 'projects', :action => 'index'
178 project_views.connect 'projects.:format', :action => 'index'
179 project_views.connect 'projects.:format', :action => 'index'
179 project_views.connect 'projects/new', :action => 'add'
180 project_views.connect 'projects/new', :action => 'add'
180 project_views.connect 'projects/:id', :action => 'show'
181 project_views.connect 'projects/:id', :action => 'show'
181 project_views.connect 'projects/:id/:action', :action => /roadmap|changelog|destroy|settings/
182 project_views.connect 'projects/:id/:action', :action => /roadmap|changelog|destroy|settings/
182 project_views.connect 'projects/:id/files', :action => 'list_files'
183 project_views.connect 'projects/:id/files', :action => 'list_files'
183 project_views.connect 'projects/:id/files/new', :action => 'add_file'
184 project_views.connect 'projects/:id/files/new', :action => 'add_file'
184 project_views.connect 'projects/:id/versions/new', :action => 'add_version'
185 project_views.connect 'projects/:id/versions/new', :action => 'add_version'
185 project_views.connect 'projects/:id/categories/new', :action => 'add_issue_category'
186 project_views.connect 'projects/:id/categories/new', :action => 'add_issue_category'
186 project_views.connect 'projects/:id/settings/:tab', :action => 'settings'
187 project_views.connect 'projects/:id/settings/:tab', :action => 'settings'
187 end
188 end
188
189
189 projects.with_options :action => 'activity', :conditions => {:method => :get} do |activity|
190 projects.with_options :action => 'activity', :conditions => {:method => :get} do |activity|
190 activity.connect 'projects/:id/activity'
191 activity.connect 'projects/:id/activity'
191 activity.connect 'projects/:id/activity.:format'
192 activity.connect 'projects/:id/activity.:format'
192 activity.connect 'activity', :id => nil
193 activity.connect 'activity', :id => nil
193 activity.connect 'activity.:format', :id => nil
194 activity.connect 'activity.:format', :id => nil
194 end
195 end
195
196
196 projects.with_options :conditions => {:method => :post} do |project_actions|
197 projects.with_options :conditions => {:method => :post} do |project_actions|
197 project_actions.connect 'projects/new', :action => 'add'
198 project_actions.connect 'projects/new', :action => 'add'
198 project_actions.connect 'projects', :action => 'add'
199 project_actions.connect 'projects', :action => 'add'
199 project_actions.connect 'projects/:id/:action', :action => /destroy|archive|unarchive/
200 project_actions.connect 'projects/:id/:action', :action => /destroy|archive|unarchive/
200 project_actions.connect 'projects/:id/files/new', :action => 'add_file'
201 project_actions.connect 'projects/:id/files/new', :action => 'add_file'
201 project_actions.connect 'projects/:id/versions/new', :action => 'add_version'
202 project_actions.connect 'projects/:id/versions/new', :action => 'add_version'
202 project_actions.connect 'projects/:id/categories/new', :action => 'add_issue_category'
203 project_actions.connect 'projects/:id/categories/new', :action => 'add_issue_category'
203 project_actions.connect 'projects/:id/activities/save', :action => 'save_activities'
204 project_actions.connect 'projects/:id/activities/save', :action => 'save_activities'
204 end
205 end
205
206
206 projects.with_options :conditions => {:method => :delete} do |project_actions|
207 projects.with_options :conditions => {:method => :delete} do |project_actions|
207 project_actions.conditions 'projects/:id/reset_activities', :action => 'reset_activities'
208 project_actions.conditions 'projects/:id/reset_activities', :action => 'reset_activities'
208 end
209 end
209 end
210 end
210
211
211 map.with_options :controller => 'repositories' do |repositories|
212 map.with_options :controller => 'repositories' do |repositories|
212 repositories.with_options :conditions => {:method => :get} do |repository_views|
213 repositories.with_options :conditions => {:method => :get} do |repository_views|
213 repository_views.connect 'projects/:id/repository', :action => 'show'
214 repository_views.connect 'projects/:id/repository', :action => 'show'
214 repository_views.connect 'projects/:id/repository/edit', :action => 'edit'
215 repository_views.connect 'projects/:id/repository/edit', :action => 'edit'
215 repository_views.connect 'projects/:id/repository/statistics', :action => 'stats'
216 repository_views.connect 'projects/:id/repository/statistics', :action => 'stats'
216 repository_views.connect 'projects/:id/repository/revisions', :action => 'revisions'
217 repository_views.connect 'projects/:id/repository/revisions', :action => 'revisions'
217 repository_views.connect 'projects/:id/repository/revisions.:format', :action => 'revisions'
218 repository_views.connect 'projects/:id/repository/revisions.:format', :action => 'revisions'
218 repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision'
219 repository_views.connect 'projects/:id/repository/revisions/:rev', :action => 'revision'
219 repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff'
220 repository_views.connect 'projects/:id/repository/revisions/:rev/diff', :action => 'diff'
220 repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff'
221 repository_views.connect 'projects/:id/repository/revisions/:rev/diff.:format', :action => 'diff'
221 repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path', :requirements => { :rev => /[a-z0-9\.\-_]+/ }
222 repository_views.connect 'projects/:id/repository/revisions/:rev/:action/*path', :requirements => { :rev => /[a-z0-9\.\-_]+/ }
222 repository_views.connect 'projects/:id/repository/:action/*path'
223 repository_views.connect 'projects/:id/repository/:action/*path'
223 end
224 end
224
225
225 repositories.connect 'projects/:id/repository/:action', :conditions => {:method => :post}
226 repositories.connect 'projects/:id/repository/:action', :conditions => {:method => :post}
226 end
227 end
227
228
228 map.connect 'attachments/:id', :controller => 'attachments', :action => 'show', :id => /\d+/
229 map.connect 'attachments/:id', :controller => 'attachments', :action => 'show', :id => /\d+/
229 map.connect 'attachments/:id/:filename', :controller => 'attachments', :action => 'show', :id => /\d+/, :filename => /.*/
230 map.connect 'attachments/:id/:filename', :controller => 'attachments', :action => 'show', :id => /\d+/, :filename => /.*/
230 map.connect 'attachments/download/:id/:filename', :controller => 'attachments', :action => 'download', :id => /\d+/, :filename => /.*/
231 map.connect 'attachments/download/:id/:filename', :controller => 'attachments', :action => 'download', :id => /\d+/, :filename => /.*/
231
232
232 map.resources :groups
233 map.resources :groups
233
234
234 #left old routes at the bottom for backwards compat
235 #left old routes at the bottom for backwards compat
235 map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
236 map.connect 'projects/:project_id/issues/:action', :controller => 'issues'
236 map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
237 map.connect 'projects/:project_id/documents/:action', :controller => 'documents'
237 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
238 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
238 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
239 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
239 map.connect 'wiki/:id/:page/:action', :page => nil, :controller => 'wiki'
240 map.connect 'wiki/:id/:page/:action', :page => nil, :controller => 'wiki'
240 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
241 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
241 map.connect 'projects/:project_id/news/:action', :controller => 'news'
242 map.connect 'projects/:project_id/news/:action', :controller => 'news'
242 map.connect 'projects/:project_id/timelog/:action/:id', :controller => 'timelog', :project_id => /.+/
243 map.connect 'projects/:project_id/timelog/:action/:id', :controller => 'timelog', :project_id => /.+/
243 map.with_options :controller => 'repositories' do |omap|
244 map.with_options :controller => 'repositories' do |omap|
244 omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
245 omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
245 omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
246 omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
246 omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
247 omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
247 omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
248 omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
248 omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate'
249 omap.repositories_entry 'repositories/annotate/:id/*path', :action => 'annotate'
249 omap.connect 'repositories/revision/:id/:rev', :action => 'revision'
250 omap.connect 'repositories/revision/:id/:rev', :action => 'revision'
250 end
251 end
251
252
252 map.with_options :controller => 'sys' do |sys|
253 map.with_options :controller => 'sys' do |sys|
253 sys.connect 'sys/projects.:format', :action => 'projects', :conditions => {:method => :get}
254 sys.connect 'sys/projects.:format', :action => 'projects', :conditions => {:method => :get}
254 sys.connect 'sys/projects/:id/repository.:format', :action => 'create_project_repository', :conditions => {:method => :post}
255 sys.connect 'sys/projects/:id/repository.:format', :action => 'create_project_repository', :conditions => {:method => :post}
255 end
256 end
256
257
257 # Install the default route as the lowest priority.
258 # Install the default route as the lowest priority.
258 map.connect ':controller/:action/:id'
259 map.connect ':controller/:action/:id'
259 map.connect 'robots.txt', :controller => 'welcome', :action => 'robots'
260 map.connect 'robots.txt', :controller => 'welcome', :action => 'robots'
260 # Used for OpenID
261 # Used for OpenID
261 map.root :controller => 'account', :action => 'login'
262 map.root :controller => 'account', :action => 'login'
262 end
263 end
@@ -1,186 +1,156
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'account_controller'
19 require 'account_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class AccountController; def rescue_action(e) raise e end; end
22 class AccountController; def rescue_action(e) raise e end; end
23
23
24 class AccountControllerTest < ActionController::TestCase
24 class AccountControllerTest < ActionController::TestCase
25 fixtures :users, :roles
25 fixtures :users, :roles
26
26
27 def setup
27 def setup
28 @controller = AccountController.new
28 @controller = AccountController.new
29 @request = ActionController::TestRequest.new
29 @request = ActionController::TestRequest.new
30 @response = ActionController::TestResponse.new
30 @response = ActionController::TestResponse.new
31 User.current = nil
31 User.current = nil
32 end
32 end
33
33
34 def test_show
35 get :show, :id => 2
36 assert_response :success
37 assert_template 'show'
38 assert_not_nil assigns(:user)
39 end
40
41 def test_show_should_not_fail_when_custom_values_are_nil
42 user = User.find(2)
43
44 # Create a custom field to illustrate the issue
45 custom_field = CustomField.create!(:name => 'Testing', :field_format => 'text')
46 custom_value = user.custom_values.build(:custom_field => custom_field).save!
47
48 get :show, :id => 2
49 assert_response :success
50 end
51
52
53 def test_show_inactive
54 get :show, :id => 5
55 assert_response 404
56 assert_nil assigns(:user)
57 end
58
59 def test_show_should_not_reveal_users_with_no_visible_activity_or_project
60 get :show, :id => 9
61 assert_response 404
62 end
63
64 def test_login_should_redirect_to_back_url_param
34 def test_login_should_redirect_to_back_url_param
65 # request.uri is "test.host" in test environment
35 # request.uri is "test.host" in test environment
66 post :login, :username => 'jsmith', :password => 'jsmith', :back_url => 'http%3A%2F%2Ftest.host%2Fissues%2Fshow%2F1'
36 post :login, :username => 'jsmith', :password => 'jsmith', :back_url => 'http%3A%2F%2Ftest.host%2Fissues%2Fshow%2F1'
67 assert_redirected_to '/issues/show/1'
37 assert_redirected_to '/issues/show/1'
68 end
38 end
69
39
70 def test_login_should_not_redirect_to_another_host
40 def test_login_should_not_redirect_to_another_host
71 post :login, :username => 'jsmith', :password => 'jsmith', :back_url => 'http%3A%2F%2Ftest.foo%2Ffake'
41 post :login, :username => 'jsmith', :password => 'jsmith', :back_url => 'http%3A%2F%2Ftest.foo%2Ffake'
72 assert_redirected_to '/my/page'
42 assert_redirected_to '/my/page'
73 end
43 end
74
44
75 def test_login_with_wrong_password
45 def test_login_with_wrong_password
76 post :login, :username => 'admin', :password => 'bad'
46 post :login, :username => 'admin', :password => 'bad'
77 assert_response :success
47 assert_response :success
78 assert_template 'login'
48 assert_template 'login'
79 assert_tag 'div',
49 assert_tag 'div',
80 :attributes => { :class => "flash error" },
50 :attributes => { :class => "flash error" },
81 :content => /Invalid user or password/
51 :content => /Invalid user or password/
82 end
52 end
83
53
84 if Object.const_defined?(:OpenID)
54 if Object.const_defined?(:OpenID)
85
55
86 def test_login_with_openid_for_existing_user
56 def test_login_with_openid_for_existing_user
87 Setting.self_registration = '3'
57 Setting.self_registration = '3'
88 Setting.openid = '1'
58 Setting.openid = '1'
89 existing_user = User.new(:firstname => 'Cool',
59 existing_user = User.new(:firstname => 'Cool',
90 :lastname => 'User',
60 :lastname => 'User',
91 :mail => 'user@somedomain.com',
61 :mail => 'user@somedomain.com',
92 :identity_url => 'http://openid.example.com/good_user')
62 :identity_url => 'http://openid.example.com/good_user')
93 existing_user.login = 'cool_user'
63 existing_user.login = 'cool_user'
94 assert existing_user.save!
64 assert existing_user.save!
95
65
96 post :login, :openid_url => existing_user.identity_url
66 post :login, :openid_url => existing_user.identity_url
97 assert_redirected_to 'my/page'
67 assert_redirected_to 'my/page'
98 end
68 end
99
69
100 def test_login_with_openid_for_existing_non_active_user
70 def test_login_with_openid_for_existing_non_active_user
101 Setting.self_registration = '2'
71 Setting.self_registration = '2'
102 Setting.openid = '1'
72 Setting.openid = '1'
103 existing_user = User.new(:firstname => 'Cool',
73 existing_user = User.new(:firstname => 'Cool',
104 :lastname => 'User',
74 :lastname => 'User',
105 :mail => 'user@somedomain.com',
75 :mail => 'user@somedomain.com',
106 :identity_url => 'http://openid.example.com/good_user',
76 :identity_url => 'http://openid.example.com/good_user',
107 :status => User::STATUS_REGISTERED)
77 :status => User::STATUS_REGISTERED)
108 existing_user.login = 'cool_user'
78 existing_user.login = 'cool_user'
109 assert existing_user.save!
79 assert existing_user.save!
110
80
111 post :login, :openid_url => existing_user.identity_url
81 post :login, :openid_url => existing_user.identity_url
112 assert_redirected_to 'login'
82 assert_redirected_to 'login'
113 end
83 end
114
84
115 def test_login_with_openid_with_new_user_created
85 def test_login_with_openid_with_new_user_created
116 Setting.self_registration = '3'
86 Setting.self_registration = '3'
117 Setting.openid = '1'
87 Setting.openid = '1'
118 post :login, :openid_url => 'http://openid.example.com/good_user'
88 post :login, :openid_url => 'http://openid.example.com/good_user'
119 assert_redirected_to 'my/account'
89 assert_redirected_to 'my/account'
120 user = User.find_by_login('cool_user')
90 user = User.find_by_login('cool_user')
121 assert user
91 assert user
122 assert_equal 'Cool', user.firstname
92 assert_equal 'Cool', user.firstname
123 assert_equal 'User', user.lastname
93 assert_equal 'User', user.lastname
124 end
94 end
125
95
126 def test_login_with_openid_with_new_user_and_self_registration_off
96 def test_login_with_openid_with_new_user_and_self_registration_off
127 Setting.self_registration = '0'
97 Setting.self_registration = '0'
128 Setting.openid = '1'
98 Setting.openid = '1'
129 post :login, :openid_url => 'http://openid.example.com/good_user'
99 post :login, :openid_url => 'http://openid.example.com/good_user'
130 assert_redirected_to home_url
100 assert_redirected_to home_url
131 user = User.find_by_login('cool_user')
101 user = User.find_by_login('cool_user')
132 assert ! user
102 assert ! user
133 end
103 end
134
104
135 def test_login_with_openid_with_new_user_created_with_email_activation_should_have_a_token
105 def test_login_with_openid_with_new_user_created_with_email_activation_should_have_a_token
136 Setting.self_registration = '1'
106 Setting.self_registration = '1'
137 Setting.openid = '1'
107 Setting.openid = '1'
138 post :login, :openid_url => 'http://openid.example.com/good_user'
108 post :login, :openid_url => 'http://openid.example.com/good_user'
139 assert_redirected_to 'login'
109 assert_redirected_to 'login'
140 user = User.find_by_login('cool_user')
110 user = User.find_by_login('cool_user')
141 assert user
111 assert user
142
112
143 token = Token.find_by_user_id_and_action(user.id, 'register')
113 token = Token.find_by_user_id_and_action(user.id, 'register')
144 assert token
114 assert token
145 end
115 end
146
116
147 def test_login_with_openid_with_new_user_created_with_manual_activation
117 def test_login_with_openid_with_new_user_created_with_manual_activation
148 Setting.self_registration = '2'
118 Setting.self_registration = '2'
149 Setting.openid = '1'
119 Setting.openid = '1'
150 post :login, :openid_url => 'http://openid.example.com/good_user'
120 post :login, :openid_url => 'http://openid.example.com/good_user'
151 assert_redirected_to 'login'
121 assert_redirected_to 'login'
152 user = User.find_by_login('cool_user')
122 user = User.find_by_login('cool_user')
153 assert user
123 assert user
154 assert_equal User::STATUS_REGISTERED, user.status
124 assert_equal User::STATUS_REGISTERED, user.status
155 end
125 end
156
126
157 def test_login_with_openid_with_new_user_with_conflict_should_register
127 def test_login_with_openid_with_new_user_with_conflict_should_register
158 Setting.self_registration = '3'
128 Setting.self_registration = '3'
159 Setting.openid = '1'
129 Setting.openid = '1'
160 existing_user = User.new(:firstname => 'Cool', :lastname => 'User', :mail => 'user@somedomain.com')
130 existing_user = User.new(:firstname => 'Cool', :lastname => 'User', :mail => 'user@somedomain.com')
161 existing_user.login = 'cool_user'
131 existing_user.login = 'cool_user'
162 assert existing_user.save!
132 assert existing_user.save!
163
133
164 post :login, :openid_url => 'http://openid.example.com/good_user'
134 post :login, :openid_url => 'http://openid.example.com/good_user'
165 assert_response :success
135 assert_response :success
166 assert_template 'register'
136 assert_template 'register'
167 assert assigns(:user)
137 assert assigns(:user)
168 assert_equal 'http://openid.example.com/good_user', assigns(:user)[:identity_url]
138 assert_equal 'http://openid.example.com/good_user', assigns(:user)[:identity_url]
169 end
139 end
170
140
171 def test_setting_openid_should_return_true_when_set_to_true
141 def test_setting_openid_should_return_true_when_set_to_true
172 Setting.openid = '1'
142 Setting.openid = '1'
173 assert_equal true, Setting.openid?
143 assert_equal true, Setting.openid?
174 end
144 end
175
145
176 else
146 else
177 puts "Skipping openid tests."
147 puts "Skipping openid tests."
178 end
148 end
179
149
180 def test_logout
150 def test_logout
181 @request.session[:user_id] = 2
151 @request.session[:user_id] = 2
182 get :logout
152 get :logout
183 assert_redirected_to ''
153 assert_redirected_to ''
184 assert_nil @request.session[:user_id]
154 assert_nil @request.session[:user_id]
185 end
155 end
186 end
156 end
@@ -1,181 +1,224
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'users_controller'
19 require 'users_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class UsersController; def rescue_action(e) raise e end; end
22 class UsersController; def rescue_action(e) raise e end; end
23
23
24 class UsersControllerTest < ActionController::TestCase
24 class UsersControllerTest < ActionController::TestCase
25 include Redmine::I18n
25 include Redmine::I18n
26
26
27 fixtures :users, :projects, :members, :member_roles, :roles
27 fixtures :users, :projects, :members, :member_roles, :roles
28
28
29 def setup
29 def setup
30 @controller = UsersController.new
30 @controller = UsersController.new
31 @request = ActionController::TestRequest.new
31 @request = ActionController::TestRequest.new
32 @response = ActionController::TestResponse.new
32 @response = ActionController::TestResponse.new
33 User.current = nil
33 User.current = nil
34 @request.session[:user_id] = 1 # admin
34 @request.session[:user_id] = 1 # admin
35 end
35 end
36
36
37 def test_index_routing
37 def test_index_routing
38 #TODO: unify with list
38 #TODO: unify with list
39 assert_generates(
39 assert_generates(
40 '/users',
40 '/users',
41 :controller => 'users', :action => 'index'
41 :controller => 'users', :action => 'index'
42 )
42 )
43 end
43 end
44
44
45 def test_index
45 def test_index
46 get :index
46 get :index
47 assert_response :success
47 assert_response :success
48 assert_template 'list'
48 assert_template 'list'
49 end
49 end
50
50
51 def test_list_routing
51 def test_list_routing
52 #TODO: rename action to index
52 #TODO: rename action to index
53 assert_routing(
53 assert_routing(
54 {:method => :get, :path => '/users'},
54 {:method => :get, :path => '/users'},
55 :controller => 'users', :action => 'list'
55 :controller => 'users', :action => 'list'
56 )
56 )
57 end
57 end
58
58
59 def test_list
59 def test_list
60 get :list
60 get :list
61 assert_response :success
61 assert_response :success
62 assert_template 'list'
62 assert_template 'list'
63 assert_not_nil assigns(:users)
63 assert_not_nil assigns(:users)
64 # active users only
64 # active users only
65 assert_nil assigns(:users).detect {|u| !u.active?}
65 assert_nil assigns(:users).detect {|u| !u.active?}
66 end
66 end
67
67
68 def test_list_with_name_filter
68 def test_list_with_name_filter
69 get :list, :name => 'john'
69 get :list, :name => 'john'
70 assert_response :success
70 assert_response :success
71 assert_template 'list'
71 assert_template 'list'
72 users = assigns(:users)
72 users = assigns(:users)
73 assert_not_nil users
73 assert_not_nil users
74 assert_equal 1, users.size
74 assert_equal 1, users.size
75 assert_equal 'John', users.first.firstname
75 assert_equal 'John', users.first.firstname
76 end
76 end
77
77
78 def test_show_routing
79 assert_routing(
80 {:method => :get, :path => '/users/44'},
81 :controller => 'users', :action => 'show', :id => '44'
82 )
83 assert_recognizes(
84 {:controller => 'users', :action => 'show', :id => '44'},
85 {:method => :get, :path => '/users/44'}
86 )
87 end
88
89 def test_show
90 @request.session[:user_id] = nil
91 get :show, :id => 2
92 assert_response :success
93 assert_template 'show'
94 assert_not_nil assigns(:user)
95 end
96
97 def test_show_should_not_fail_when_custom_values_are_nil
98 user = User.find(2)
99
100 # Create a custom field to illustrate the issue
101 custom_field = CustomField.create!(:name => 'Testing', :field_format => 'text')
102 custom_value = user.custom_values.build(:custom_field => custom_field).save!
103
104 get :show, :id => 2
105 assert_response :success
106 end
107
108
109 def test_show_inactive
110 get :show, :id => 5
111 assert_response 404
112 assert_nil assigns(:user)
113 end
114
115 def test_show_should_not_reveal_users_with_no_visible_activity_or_project
116 @request.session[:user_id] = nil
117 get :show, :id => 9
118 assert_response 404
119 end
120
78 def test_add_routing
121 def test_add_routing
79 assert_routing(
122 assert_routing(
80 {:method => :get, :path => '/users/new'},
123 {:method => :get, :path => '/users/new'},
81 :controller => 'users', :action => 'add'
124 :controller => 'users', :action => 'add'
82 )
125 )
83 assert_recognizes(
126 assert_recognizes(
84 #TODO: remove this and replace with POST to collection, need to modify form
127 #TODO: remove this and replace with POST to collection, need to modify form
85 {:controller => 'users', :action => 'add'},
128 {:controller => 'users', :action => 'add'},
86 {:method => :post, :path => '/users/new'}
129 {:method => :post, :path => '/users/new'}
87 )
130 )
88 assert_recognizes(
131 assert_recognizes(
89 {:controller => 'users', :action => 'add'},
132 {:controller => 'users', :action => 'add'},
90 {:method => :post, :path => '/users'}
133 {:method => :post, :path => '/users'}
91 )
134 )
92 end
135 end
93
136
94 def test_edit_routing
137 def test_edit_routing
95 assert_routing(
138 assert_routing(
96 {:method => :get, :path => '/users/444/edit'},
139 {:method => :get, :path => '/users/444/edit'},
97 :controller => 'users', :action => 'edit', :id => '444'
140 :controller => 'users', :action => 'edit', :id => '444'
98 )
141 )
99 assert_routing(
142 assert_routing(
100 {:method => :get, :path => '/users/222/edit/membership'},
143 {:method => :get, :path => '/users/222/edit/membership'},
101 :controller => 'users', :action => 'edit', :id => '222', :tab => 'membership'
144 :controller => 'users', :action => 'edit', :id => '222', :tab => 'membership'
102 )
145 )
103 assert_recognizes(
146 assert_recognizes(
104 #TODO: use PUT on user_path, modify form
147 #TODO: use PUT on user_path, modify form
105 {:controller => 'users', :action => 'edit', :id => '444'},
148 {:controller => 'users', :action => 'edit', :id => '444'},
106 {:method => :post, :path => '/users/444/edit'}
149 {:method => :post, :path => '/users/444/edit'}
107 )
150 )
108 end
151 end
109
152
110 def test_edit
153 def test_edit
111 ActionMailer::Base.deliveries.clear
154 ActionMailer::Base.deliveries.clear
112 post :edit, :id => 2, :user => {:firstname => 'Changed'}
155 post :edit, :id => 2, :user => {:firstname => 'Changed'}
113 assert_equal 'Changed', User.find(2).firstname
156 assert_equal 'Changed', User.find(2).firstname
114 assert ActionMailer::Base.deliveries.empty?
157 assert ActionMailer::Base.deliveries.empty?
115 end
158 end
116
159
117 def test_edit_with_activation_should_send_a_notification
160 def test_edit_with_activation_should_send_a_notification
118 u = User.new(:firstname => 'Foo', :lastname => 'Bar', :mail => 'foo.bar@somenet.foo', :language => 'fr')
161 u = User.new(:firstname => 'Foo', :lastname => 'Bar', :mail => 'foo.bar@somenet.foo', :language => 'fr')
119 u.login = 'foo'
162 u.login = 'foo'
120 u.status = User::STATUS_REGISTERED
163 u.status = User::STATUS_REGISTERED
121 u.save!
164 u.save!
122 ActionMailer::Base.deliveries.clear
165 ActionMailer::Base.deliveries.clear
123 Setting.bcc_recipients = '1'
166 Setting.bcc_recipients = '1'
124
167
125 post :edit, :id => u.id, :user => {:status => User::STATUS_ACTIVE}
168 post :edit, :id => u.id, :user => {:status => User::STATUS_ACTIVE}
126 assert u.reload.active?
169 assert u.reload.active?
127 mail = ActionMailer::Base.deliveries.last
170 mail = ActionMailer::Base.deliveries.last
128 assert_not_nil mail
171 assert_not_nil mail
129 assert_equal ['foo.bar@somenet.foo'], mail.bcc
172 assert_equal ['foo.bar@somenet.foo'], mail.bcc
130 assert mail.body.include?(ll('fr', :notice_account_activated))
173 assert mail.body.include?(ll('fr', :notice_account_activated))
131 end
174 end
132
175
133 def test_edit_with_password_change_should_send_a_notification
176 def test_edit_with_password_change_should_send_a_notification
134 ActionMailer::Base.deliveries.clear
177 ActionMailer::Base.deliveries.clear
135 Setting.bcc_recipients = '1'
178 Setting.bcc_recipients = '1'
136
179
137 u = User.find(2)
180 u = User.find(2)
138 post :edit, :id => u.id, :user => {}, :password => 'newpass', :password_confirmation => 'newpass', :send_information => '1'
181 post :edit, :id => u.id, :user => {}, :password => 'newpass', :password_confirmation => 'newpass', :send_information => '1'
139 assert_equal User.hash_password('newpass'), u.reload.hashed_password
182 assert_equal User.hash_password('newpass'), u.reload.hashed_password
140
183
141 mail = ActionMailer::Base.deliveries.last
184 mail = ActionMailer::Base.deliveries.last
142 assert_not_nil mail
185 assert_not_nil mail
143 assert_equal [u.mail], mail.bcc
186 assert_equal [u.mail], mail.bcc
144 assert mail.body.include?('newpass')
187 assert mail.body.include?('newpass')
145 end
188 end
146
189
147 def test_add_membership_routing
190 def test_add_membership_routing
148 assert_routing(
191 assert_routing(
149 {:method => :post, :path => '/users/123/memberships'},
192 {:method => :post, :path => '/users/123/memberships'},
150 :controller => 'users', :action => 'edit_membership', :id => '123'
193 :controller => 'users', :action => 'edit_membership', :id => '123'
151 )
194 )
152 end
195 end
153
196
154 def test_edit_membership_routing
197 def test_edit_membership_routing
155 assert_routing(
198 assert_routing(
156 {:method => :post, :path => '/users/123/memberships/55'},
199 {:method => :post, :path => '/users/123/memberships/55'},
157 :controller => 'users', :action => 'edit_membership', :id => '123', :membership_id => '55'
200 :controller => 'users', :action => 'edit_membership', :id => '123', :membership_id => '55'
158 )
201 )
159 end
202 end
160
203
161 def test_edit_membership
204 def test_edit_membership
162 post :edit_membership, :id => 2, :membership_id => 1,
205 post :edit_membership, :id => 2, :membership_id => 1,
163 :membership => { :role_ids => [2]}
206 :membership => { :role_ids => [2]}
164 assert_redirected_to :action => 'edit', :id => '2', :tab => 'memberships'
207 assert_redirected_to :action => 'edit', :id => '2', :tab => 'memberships'
165 assert_equal [2], Member.find(1).role_ids
208 assert_equal [2], Member.find(1).role_ids
166 end
209 end
167
210
168 def test_destroy_membership
211 def test_destroy_membership
169 assert_routing(
212 assert_routing(
170 #TODO: use DELETE method on user_membership_path, modify form
213 #TODO: use DELETE method on user_membership_path, modify form
171 {:method => :post, :path => '/users/567/memberships/12/destroy'},
214 {:method => :post, :path => '/users/567/memberships/12/destroy'},
172 :controller => 'users', :action => 'destroy_membership', :id => '567', :membership_id => '12'
215 :controller => 'users', :action => 'destroy_membership', :id => '567', :membership_id => '12'
173 )
216 )
174 end
217 end
175
218
176 def test_destroy_membership
219 def test_destroy_membership
177 post :destroy_membership, :id => 2, :membership_id => 1
220 post :destroy_membership, :id => 2, :membership_id => 1
178 assert_redirected_to :action => 'edit', :id => '2', :tab => 'memberships'
221 assert_redirected_to :action => 'edit', :id => '2', :tab => 'memberships'
179 assert_nil Member.find_by_id(1)
222 assert_nil Member.find_by_id(1)
180 end
223 end
181 end
224 end
General Comments 0
You need to be logged in to leave comments. Login now