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