##// END OF EJS Templates
Removed calls to deprecated Project.visible_by method....
Jean-Philippe Lang -
r5208:5f7f69e21459
parent child
Show More
@@ -1,240 +1,240
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class UsersController < ApplicationController
18 class UsersController < ApplicationController
19 layout 'admin'
19 layout 'admin'
20
20
21 before_filter :require_admin, :except => :show
21 before_filter :require_admin, :except => :show
22 before_filter :find_user, :only => [:show, :edit, :update, :destroy, :edit_membership, :destroy_membership]
22 before_filter :find_user, :only => [:show, :edit, :update, :destroy, :edit_membership, :destroy_membership]
23 accept_key_auth :index, :show, :create, :update, :destroy
23 accept_key_auth :index, :show, :create, :update, :destroy
24
24
25 helper :sort
25 helper :sort
26 include SortHelper
26 include SortHelper
27 helper :custom_fields
27 helper :custom_fields
28 include CustomFieldsHelper
28 include CustomFieldsHelper
29
29
30 def index
30 def index
31 sort_init 'login', 'asc'
31 sort_init 'login', 'asc'
32 sort_update %w(login firstname lastname mail admin created_on last_login_on)
32 sort_update %w(login firstname lastname mail admin created_on last_login_on)
33
33
34 case params[:format]
34 case params[:format]
35 when 'xml', 'json'
35 when 'xml', 'json'
36 @offset, @limit = api_offset_and_limit
36 @offset, @limit = api_offset_and_limit
37 else
37 else
38 @limit = per_page_option
38 @limit = per_page_option
39 end
39 end
40
40
41 scope = User
41 scope = User
42 scope = scope.in_group(params[:group_id].to_i) if params[:group_id].present?
42 scope = scope.in_group(params[:group_id].to_i) if params[:group_id].present?
43
43
44 @status = params[:status] ? params[:status].to_i : 1
44 @status = params[:status] ? params[:status].to_i : 1
45 c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
45 c = ARCondition.new(@status == 0 ? "status <> 0" : ["status = ?", @status])
46
46
47 unless params[:name].blank?
47 unless params[:name].blank?
48 name = "%#{params[:name].strip.downcase}%"
48 name = "%#{params[:name].strip.downcase}%"
49 c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ? OR LOWER(mail) LIKE ?", name, name, name, name]
49 c << ["LOWER(login) LIKE ? OR LOWER(firstname) LIKE ? OR LOWER(lastname) LIKE ? OR LOWER(mail) LIKE ?", name, name, name, name]
50 end
50 end
51
51
52 @user_count = scope.count(:conditions => c.conditions)
52 @user_count = scope.count(:conditions => c.conditions)
53 @user_pages = Paginator.new self, @user_count, @limit, params['page']
53 @user_pages = Paginator.new self, @user_count, @limit, params['page']
54 @offset ||= @user_pages.current.offset
54 @offset ||= @user_pages.current.offset
55 @users = scope.find :all,
55 @users = scope.find :all,
56 :order => sort_clause,
56 :order => sort_clause,
57 :conditions => c.conditions,
57 :conditions => c.conditions,
58 :limit => @limit,
58 :limit => @limit,
59 :offset => @offset
59 :offset => @offset
60
60
61 respond_to do |format|
61 respond_to do |format|
62 format.html {
62 format.html {
63 @groups = Group.all.sort
63 @groups = Group.all.sort
64 render :layout => !request.xhr?
64 render :layout => !request.xhr?
65 }
65 }
66 format.api
66 format.api
67 end
67 end
68 end
68 end
69
69
70 def show
70 def show
71 # show projects based on current user visibility
71 # show projects based on current user visibility
72 @memberships = @user.memberships.all(:conditions => Project.visible_by(User.current))
72 @memberships = @user.memberships.all(:conditions => Project.visible_condition(User.current))
73
73
74 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
74 events = Redmine::Activity::Fetcher.new(User.current, :author => @user).events(nil, nil, :limit => 10)
75 @events_by_day = events.group_by(&:event_date)
75 @events_by_day = events.group_by(&:event_date)
76
76
77 unless User.current.admin?
77 unless User.current.admin?
78 if !@user.active? || (@user != User.current && @memberships.empty? && events.empty?)
78 if !@user.active? || (@user != User.current && @memberships.empty? && events.empty?)
79 render_404
79 render_404
80 return
80 return
81 end
81 end
82 end
82 end
83
83
84 respond_to do |format|
84 respond_to do |format|
85 format.html { render :layout => 'base' }
85 format.html { render :layout => 'base' }
86 format.api
86 format.api
87 end
87 end
88 end
88 end
89
89
90 def new
90 def new
91 @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
91 @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
92 @auth_sources = AuthSource.find(:all)
92 @auth_sources = AuthSource.find(:all)
93 end
93 end
94
94
95 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
95 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
96 def create
96 def create
97 @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
97 @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option)
98 @user.safe_attributes = params[:user]
98 @user.safe_attributes = params[:user]
99 @user.admin = params[:user][:admin] || false
99 @user.admin = params[:user][:admin] || false
100 @user.login = params[:user][:login]
100 @user.login = params[:user][:login]
101 @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
101 @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id
102
102
103 # TODO: Similar to My#account
103 # TODO: Similar to My#account
104 @user.pref.attributes = params[:pref]
104 @user.pref.attributes = params[:pref]
105 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
105 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
106
106
107 if @user.save
107 if @user.save
108 @user.pref.save
108 @user.pref.save
109 @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
109 @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
110
110
111 Mailer.deliver_account_information(@user, params[:user][:password]) if params[:send_information]
111 Mailer.deliver_account_information(@user, params[:user][:password]) if params[:send_information]
112
112
113 respond_to do |format|
113 respond_to do |format|
114 format.html {
114 format.html {
115 flash[:notice] = l(:notice_successful_create)
115 flash[:notice] = l(:notice_successful_create)
116 redirect_to(params[:continue] ?
116 redirect_to(params[:continue] ?
117 {:controller => 'users', :action => 'new'} :
117 {:controller => 'users', :action => 'new'} :
118 {:controller => 'users', :action => 'edit', :id => @user}
118 {:controller => 'users', :action => 'edit', :id => @user}
119 )
119 )
120 }
120 }
121 format.api { render :action => 'show', :status => :created, :location => user_url(@user) }
121 format.api { render :action => 'show', :status => :created, :location => user_url(@user) }
122 end
122 end
123 else
123 else
124 @auth_sources = AuthSource.find(:all)
124 @auth_sources = AuthSource.find(:all)
125 # Clear password input
125 # Clear password input
126 @user.password = @user.password_confirmation = nil
126 @user.password = @user.password_confirmation = nil
127
127
128 respond_to do |format|
128 respond_to do |format|
129 format.html { render :action => 'new' }
129 format.html { render :action => 'new' }
130 format.api { render_validation_errors(@user) }
130 format.api { render_validation_errors(@user) }
131 end
131 end
132 end
132 end
133 end
133 end
134
134
135 def edit
135 def edit
136 @auth_sources = AuthSource.find(:all)
136 @auth_sources = AuthSource.find(:all)
137 @membership ||= Member.new
137 @membership ||= Member.new
138 end
138 end
139
139
140 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
140 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
141 def update
141 def update
142 @user.admin = params[:user][:admin] if params[:user][:admin]
142 @user.admin = params[:user][:admin] if params[:user][:admin]
143 @user.login = params[:user][:login] if params[:user][:login]
143 @user.login = params[:user][:login] if params[:user][:login]
144 if params[:user][:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
144 if params[:user][:password].present? && (@user.auth_source_id.nil? || params[:user][:auth_source_id].blank?)
145 @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
145 @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
146 end
146 end
147 @user.safe_attributes = params[:user]
147 @user.safe_attributes = params[:user]
148 # Was the account actived ? (do it before User#save clears the change)
148 # Was the account actived ? (do it before User#save clears the change)
149 was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
149 was_activated = (@user.status_change == [User::STATUS_REGISTERED, User::STATUS_ACTIVE])
150 # TODO: Similar to My#account
150 # TODO: Similar to My#account
151 @user.pref.attributes = params[:pref]
151 @user.pref.attributes = params[:pref]
152 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
152 @user.pref[:no_self_notified] = (params[:no_self_notified] == '1')
153
153
154 if @user.save
154 if @user.save
155 @user.pref.save
155 @user.pref.save
156 @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
156 @user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
157
157
158 if was_activated
158 if was_activated
159 Mailer.deliver_account_activated(@user)
159 Mailer.deliver_account_activated(@user)
160 elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
160 elsif @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.auth_source_id.nil?
161 Mailer.deliver_account_information(@user, params[:user][:password])
161 Mailer.deliver_account_information(@user, params[:user][:password])
162 end
162 end
163
163
164 respond_to do |format|
164 respond_to do |format|
165 format.html {
165 format.html {
166 flash[:notice] = l(:notice_successful_update)
166 flash[:notice] = l(:notice_successful_update)
167 redirect_to :back
167 redirect_to :back
168 }
168 }
169 format.api { head :ok }
169 format.api { head :ok }
170 end
170 end
171 else
171 else
172 @auth_sources = AuthSource.find(:all)
172 @auth_sources = AuthSource.find(:all)
173 @membership ||= Member.new
173 @membership ||= Member.new
174 # Clear password input
174 # Clear password input
175 @user.password = @user.password_confirmation = nil
175 @user.password = @user.password_confirmation = nil
176
176
177 respond_to do |format|
177 respond_to do |format|
178 format.html { render :action => :edit }
178 format.html { render :action => :edit }
179 format.api { render_validation_errors(@user) }
179 format.api { render_validation_errors(@user) }
180 end
180 end
181 end
181 end
182 rescue ::ActionController::RedirectBackError
182 rescue ::ActionController::RedirectBackError
183 redirect_to :controller => 'users', :action => 'edit', :id => @user
183 redirect_to :controller => 'users', :action => 'edit', :id => @user
184 end
184 end
185
185
186 verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
186 verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
187 def destroy
187 def destroy
188 @user.destroy
188 @user.destroy
189 respond_to do |format|
189 respond_to do |format|
190 format.html { redirect_to(users_url) }
190 format.html { redirect_to(users_url) }
191 format.api { head :ok }
191 format.api { head :ok }
192 end
192 end
193 end
193 end
194
194
195 def edit_membership
195 def edit_membership
196 @membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
196 @membership = Member.edit_membership(params[:membership_id], params[:membership], @user)
197 @membership.save if request.post?
197 @membership.save if request.post?
198 respond_to do |format|
198 respond_to do |format|
199 if @membership.valid?
199 if @membership.valid?
200 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
200 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
201 format.js {
201 format.js {
202 render(:update) {|page|
202 render(:update) {|page|
203 page.replace_html "tab-content-memberships", :partial => 'users/memberships'
203 page.replace_html "tab-content-memberships", :partial => 'users/memberships'
204 page.visual_effect(:highlight, "member-#{@membership.id}")
204 page.visual_effect(:highlight, "member-#{@membership.id}")
205 }
205 }
206 }
206 }
207 else
207 else
208 format.js {
208 format.js {
209 render(:update) {|page|
209 render(:update) {|page|
210 page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')))
210 page.alert(l(:notice_failed_to_save_members, :errors => @membership.errors.full_messages.join(', ')))
211 }
211 }
212 }
212 }
213 end
213 end
214 end
214 end
215 end
215 end
216
216
217 def destroy_membership
217 def destroy_membership
218 @membership = Member.find(params[:membership_id])
218 @membership = Member.find(params[:membership_id])
219 if request.post? && @membership.deletable?
219 if request.post? && @membership.deletable?
220 @membership.destroy
220 @membership.destroy
221 end
221 end
222 respond_to do |format|
222 respond_to do |format|
223 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
223 format.html { redirect_to :controller => 'users', :action => 'edit', :id => @user, :tab => 'memberships' }
224 format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
224 format.js { render(:update) {|page| page.replace_html "tab-content-memberships", :partial => 'users/memberships'} }
225 end
225 end
226 end
226 end
227
227
228 private
228 private
229
229
230 def find_user
230 def find_user
231 if params[:id] == 'current'
231 if params[:id] == 'current'
232 require_login || return
232 require_login || return
233 @user = User.current
233 @user = User.current
234 else
234 else
235 @user = User.find(params[:id])
235 @user = User.find(params[:id])
236 end
236 end
237 rescue ActiveRecord::RecordNotFound
237 rescue ActiveRecord::RecordNotFound
238 render_404
238 render_404
239 end
239 end
240 end
240 end
@@ -1,514 +1,514
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 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.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19 require 'projects_controller'
19 require 'projects_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class ProjectsController; def rescue_action(e) raise e end; end
22 class ProjectsController; def rescue_action(e) raise e end; end
23
23
24 class ProjectsControllerTest < ActionController::TestCase
24 class ProjectsControllerTest < ActionController::TestCase
25 fixtures :projects, :versions, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details,
25 fixtures :projects, :versions, :users, :roles, :members, :member_roles, :issues, :journals, :journal_details,
26 :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages,
26 :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages,
27 :attachments, :custom_fields, :custom_values, :time_entries
27 :attachments, :custom_fields, :custom_values, :time_entries
28
28
29 def setup
29 def setup
30 @controller = ProjectsController.new
30 @controller = ProjectsController.new
31 @request = ActionController::TestRequest.new
31 @request = ActionController::TestRequest.new
32 @response = ActionController::TestResponse.new
32 @response = ActionController::TestResponse.new
33 @request.session[:user_id] = nil
33 @request.session[:user_id] = nil
34 Setting.default_language = 'en'
34 Setting.default_language = 'en'
35 end
35 end
36
36
37 def test_index
37 def test_index
38 get :index
38 get :index
39 assert_response :success
39 assert_response :success
40 assert_template 'index'
40 assert_template 'index'
41 assert_not_nil assigns(:projects)
41 assert_not_nil assigns(:projects)
42
42
43 assert_tag :ul, :child => {:tag => 'li',
43 assert_tag :ul, :child => {:tag => 'li',
44 :descendant => {:tag => 'a', :content => 'eCookbook'},
44 :descendant => {:tag => 'a', :content => 'eCookbook'},
45 :child => { :tag => 'ul',
45 :child => { :tag => 'ul',
46 :descendant => { :tag => 'a',
46 :descendant => { :tag => 'a',
47 :content => 'Child of private child'
47 :content => 'Child of private child'
48 }
48 }
49 }
49 }
50 }
50 }
51
51
52 assert_no_tag :a, :content => /Private child of eCookbook/
52 assert_no_tag :a, :content => /Private child of eCookbook/
53 end
53 end
54
54
55 def test_index_atom
55 def test_index_atom
56 get :index, :format => 'atom'
56 get :index, :format => 'atom'
57 assert_response :success
57 assert_response :success
58 assert_template 'common/feed.atom.rxml'
58 assert_template 'common/feed.atom.rxml'
59 assert_select 'feed>title', :text => 'Redmine: Latest projects'
59 assert_select 'feed>title', :text => 'Redmine: Latest projects'
60 assert_select 'feed>entry', :count => Project.count(:conditions => Project.visible_by(User.current))
60 assert_select 'feed>entry', :count => Project.count(:conditions => Project.visible_condition(User.current))
61 end
61 end
62
62
63 context "#index" do
63 context "#index" do
64 context "by non-admin user with view_time_entries permission" do
64 context "by non-admin user with view_time_entries permission" do
65 setup do
65 setup do
66 @request.session[:user_id] = 3
66 @request.session[:user_id] = 3
67 end
67 end
68 should "show overall spent time link" do
68 should "show overall spent time link" do
69 get :index
69 get :index
70 assert_template 'index'
70 assert_template 'index'
71 assert_tag :a, :attributes => {:href => '/time_entries'}
71 assert_tag :a, :attributes => {:href => '/time_entries'}
72 end
72 end
73 end
73 end
74
74
75 context "by non-admin user without view_time_entries permission" do
75 context "by non-admin user without view_time_entries permission" do
76 setup do
76 setup do
77 Role.find(2).remove_permission! :view_time_entries
77 Role.find(2).remove_permission! :view_time_entries
78 Role.non_member.remove_permission! :view_time_entries
78 Role.non_member.remove_permission! :view_time_entries
79 Role.anonymous.remove_permission! :view_time_entries
79 Role.anonymous.remove_permission! :view_time_entries
80 @request.session[:user_id] = 3
80 @request.session[:user_id] = 3
81 end
81 end
82 should "not show overall spent time link" do
82 should "not show overall spent time link" do
83 get :index
83 get :index
84 assert_template 'index'
84 assert_template 'index'
85 assert_no_tag :a, :attributes => {:href => '/time_entries'}
85 assert_no_tag :a, :attributes => {:href => '/time_entries'}
86 end
86 end
87 end
87 end
88 end
88 end
89
89
90 context "#new" do
90 context "#new" do
91 context "by admin user" do
91 context "by admin user" do
92 setup do
92 setup do
93 @request.session[:user_id] = 1
93 @request.session[:user_id] = 1
94 end
94 end
95
95
96 should "accept get" do
96 should "accept get" do
97 get :new
97 get :new
98 assert_response :success
98 assert_response :success
99 assert_template 'new'
99 assert_template 'new'
100 end
100 end
101
101
102 end
102 end
103
103
104 context "by non-admin user with add_project permission" do
104 context "by non-admin user with add_project permission" do
105 setup do
105 setup do
106 Role.non_member.add_permission! :add_project
106 Role.non_member.add_permission! :add_project
107 @request.session[:user_id] = 9
107 @request.session[:user_id] = 9
108 end
108 end
109
109
110 should "accept get" do
110 should "accept get" do
111 get :new
111 get :new
112 assert_response :success
112 assert_response :success
113 assert_template 'new'
113 assert_template 'new'
114 assert_no_tag :select, :attributes => {:name => 'project[parent_id]'}
114 assert_no_tag :select, :attributes => {:name => 'project[parent_id]'}
115 end
115 end
116 end
116 end
117
117
118 context "by non-admin user with add_subprojects permission" do
118 context "by non-admin user with add_subprojects permission" do
119 setup do
119 setup do
120 Role.find(1).remove_permission! :add_project
120 Role.find(1).remove_permission! :add_project
121 Role.find(1).add_permission! :add_subprojects
121 Role.find(1).add_permission! :add_subprojects
122 @request.session[:user_id] = 2
122 @request.session[:user_id] = 2
123 end
123 end
124
124
125 should "accept get" do
125 should "accept get" do
126 get :new, :parent_id => 'ecookbook'
126 get :new, :parent_id => 'ecookbook'
127 assert_response :success
127 assert_response :success
128 assert_template 'new'
128 assert_template 'new'
129 # parent project selected
129 # parent project selected
130 assert_tag :select, :attributes => {:name => 'project[parent_id]'},
130 assert_tag :select, :attributes => {:name => 'project[parent_id]'},
131 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}}
131 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}}
132 # no empty value
132 # no empty value
133 assert_no_tag :select, :attributes => {:name => 'project[parent_id]'},
133 assert_no_tag :select, :attributes => {:name => 'project[parent_id]'},
134 :child => {:tag => 'option', :attributes => {:value => ''}}
134 :child => {:tag => 'option', :attributes => {:value => ''}}
135 end
135 end
136 end
136 end
137
137
138 end
138 end
139
139
140 context "POST :create" do
140 context "POST :create" do
141 context "by admin user" do
141 context "by admin user" do
142 setup do
142 setup do
143 @request.session[:user_id] = 1
143 @request.session[:user_id] = 1
144 end
144 end
145
145
146 should "create a new project" do
146 should "create a new project" do
147 post :create,
147 post :create,
148 :project => {
148 :project => {
149 :name => "blog",
149 :name => "blog",
150 :description => "weblog",
150 :description => "weblog",
151 :homepage => 'http://weblog',
151 :homepage => 'http://weblog',
152 :identifier => "blog",
152 :identifier => "blog",
153 :is_public => 1,
153 :is_public => 1,
154 :custom_field_values => { '3' => 'Beta' },
154 :custom_field_values => { '3' => 'Beta' },
155 :tracker_ids => ['1', '3'],
155 :tracker_ids => ['1', '3'],
156 # an issue custom field that is not for all project
156 # an issue custom field that is not for all project
157 :issue_custom_field_ids => ['9'],
157 :issue_custom_field_ids => ['9'],
158 :enabled_module_names => ['issue_tracking', 'news', 'repository']
158 :enabled_module_names => ['issue_tracking', 'news', 'repository']
159 }
159 }
160 assert_redirected_to '/projects/blog/settings'
160 assert_redirected_to '/projects/blog/settings'
161
161
162 project = Project.find_by_name('blog')
162 project = Project.find_by_name('blog')
163 assert_kind_of Project, project
163 assert_kind_of Project, project
164 assert project.active?
164 assert project.active?
165 assert_equal 'weblog', project.description
165 assert_equal 'weblog', project.description
166 assert_equal 'http://weblog', project.homepage
166 assert_equal 'http://weblog', project.homepage
167 assert_equal true, project.is_public?
167 assert_equal true, project.is_public?
168 assert_nil project.parent
168 assert_nil project.parent
169 assert_equal 'Beta', project.custom_value_for(3).value
169 assert_equal 'Beta', project.custom_value_for(3).value
170 assert_equal [1, 3], project.trackers.map(&:id).sort
170 assert_equal [1, 3], project.trackers.map(&:id).sort
171 assert_equal ['issue_tracking', 'news', 'repository'], project.enabled_module_names.sort
171 assert_equal ['issue_tracking', 'news', 'repository'], project.enabled_module_names.sort
172 assert project.issue_custom_fields.include?(IssueCustomField.find(9))
172 assert project.issue_custom_fields.include?(IssueCustomField.find(9))
173 end
173 end
174
174
175 should "create a new subproject" do
175 should "create a new subproject" do
176 post :create, :project => { :name => "blog",
176 post :create, :project => { :name => "blog",
177 :description => "weblog",
177 :description => "weblog",
178 :identifier => "blog",
178 :identifier => "blog",
179 :is_public => 1,
179 :is_public => 1,
180 :custom_field_values => { '3' => 'Beta' },
180 :custom_field_values => { '3' => 'Beta' },
181 :parent_id => 1
181 :parent_id => 1
182 }
182 }
183 assert_redirected_to '/projects/blog/settings'
183 assert_redirected_to '/projects/blog/settings'
184
184
185 project = Project.find_by_name('blog')
185 project = Project.find_by_name('blog')
186 assert_kind_of Project, project
186 assert_kind_of Project, project
187 assert_equal Project.find(1), project.parent
187 assert_equal Project.find(1), project.parent
188 end
188 end
189 end
189 end
190
190
191 context "by non-admin user with add_project permission" do
191 context "by non-admin user with add_project permission" do
192 setup do
192 setup do
193 Role.non_member.add_permission! :add_project
193 Role.non_member.add_permission! :add_project
194 @request.session[:user_id] = 9
194 @request.session[:user_id] = 9
195 end
195 end
196
196
197 should "accept create a Project" do
197 should "accept create a Project" do
198 post :create, :project => { :name => "blog",
198 post :create, :project => { :name => "blog",
199 :description => "weblog",
199 :description => "weblog",
200 :identifier => "blog",
200 :identifier => "blog",
201 :is_public => 1,
201 :is_public => 1,
202 :custom_field_values => { '3' => 'Beta' },
202 :custom_field_values => { '3' => 'Beta' },
203 :tracker_ids => ['1', '3'],
203 :tracker_ids => ['1', '3'],
204 :enabled_module_names => ['issue_tracking', 'news', 'repository']
204 :enabled_module_names => ['issue_tracking', 'news', 'repository']
205 }
205 }
206
206
207 assert_redirected_to '/projects/blog/settings'
207 assert_redirected_to '/projects/blog/settings'
208
208
209 project = Project.find_by_name('blog')
209 project = Project.find_by_name('blog')
210 assert_kind_of Project, project
210 assert_kind_of Project, project
211 assert_equal 'weblog', project.description
211 assert_equal 'weblog', project.description
212 assert_equal true, project.is_public?
212 assert_equal true, project.is_public?
213 assert_equal [1, 3], project.trackers.map(&:id).sort
213 assert_equal [1, 3], project.trackers.map(&:id).sort
214 assert_equal ['issue_tracking', 'news', 'repository'], project.enabled_module_names.sort
214 assert_equal ['issue_tracking', 'news', 'repository'], project.enabled_module_names.sort
215
215
216 # User should be added as a project member
216 # User should be added as a project member
217 assert User.find(9).member_of?(project)
217 assert User.find(9).member_of?(project)
218 assert_equal 1, project.members.size
218 assert_equal 1, project.members.size
219 end
219 end
220
220
221 should "fail with parent_id" do
221 should "fail with parent_id" do
222 assert_no_difference 'Project.count' do
222 assert_no_difference 'Project.count' do
223 post :create, :project => { :name => "blog",
223 post :create, :project => { :name => "blog",
224 :description => "weblog",
224 :description => "weblog",
225 :identifier => "blog",
225 :identifier => "blog",
226 :is_public => 1,
226 :is_public => 1,
227 :custom_field_values => { '3' => 'Beta' },
227 :custom_field_values => { '3' => 'Beta' },
228 :parent_id => 1
228 :parent_id => 1
229 }
229 }
230 end
230 end
231 assert_response :success
231 assert_response :success
232 project = assigns(:project)
232 project = assigns(:project)
233 assert_kind_of Project, project
233 assert_kind_of Project, project
234 assert_not_nil project.errors.on(:parent_id)
234 assert_not_nil project.errors.on(:parent_id)
235 end
235 end
236 end
236 end
237
237
238 context "by non-admin user with add_subprojects permission" do
238 context "by non-admin user with add_subprojects permission" do
239 setup do
239 setup do
240 Role.find(1).remove_permission! :add_project
240 Role.find(1).remove_permission! :add_project
241 Role.find(1).add_permission! :add_subprojects
241 Role.find(1).add_permission! :add_subprojects
242 @request.session[:user_id] = 2
242 @request.session[:user_id] = 2
243 end
243 end
244
244
245 should "create a project with a parent_id" do
245 should "create a project with a parent_id" do
246 post :create, :project => { :name => "blog",
246 post :create, :project => { :name => "blog",
247 :description => "weblog",
247 :description => "weblog",
248 :identifier => "blog",
248 :identifier => "blog",
249 :is_public => 1,
249 :is_public => 1,
250 :custom_field_values => { '3' => 'Beta' },
250 :custom_field_values => { '3' => 'Beta' },
251 :parent_id => 1
251 :parent_id => 1
252 }
252 }
253 assert_redirected_to '/projects/blog/settings'
253 assert_redirected_to '/projects/blog/settings'
254 project = Project.find_by_name('blog')
254 project = Project.find_by_name('blog')
255 end
255 end
256
256
257 should "fail without parent_id" do
257 should "fail without parent_id" do
258 assert_no_difference 'Project.count' do
258 assert_no_difference 'Project.count' do
259 post :create, :project => { :name => "blog",
259 post :create, :project => { :name => "blog",
260 :description => "weblog",
260 :description => "weblog",
261 :identifier => "blog",
261 :identifier => "blog",
262 :is_public => 1,
262 :is_public => 1,
263 :custom_field_values => { '3' => 'Beta' }
263 :custom_field_values => { '3' => 'Beta' }
264 }
264 }
265 end
265 end
266 assert_response :success
266 assert_response :success
267 project = assigns(:project)
267 project = assigns(:project)
268 assert_kind_of Project, project
268 assert_kind_of Project, project
269 assert_not_nil project.errors.on(:parent_id)
269 assert_not_nil project.errors.on(:parent_id)
270 end
270 end
271
271
272 should "fail with unauthorized parent_id" do
272 should "fail with unauthorized parent_id" do
273 assert !User.find(2).member_of?(Project.find(6))
273 assert !User.find(2).member_of?(Project.find(6))
274 assert_no_difference 'Project.count' do
274 assert_no_difference 'Project.count' do
275 post :create, :project => { :name => "blog",
275 post :create, :project => { :name => "blog",
276 :description => "weblog",
276 :description => "weblog",
277 :identifier => "blog",
277 :identifier => "blog",
278 :is_public => 1,
278 :is_public => 1,
279 :custom_field_values => { '3' => 'Beta' },
279 :custom_field_values => { '3' => 'Beta' },
280 :parent_id => 6
280 :parent_id => 6
281 }
281 }
282 end
282 end
283 assert_response :success
283 assert_response :success
284 project = assigns(:project)
284 project = assigns(:project)
285 assert_kind_of Project, project
285 assert_kind_of Project, project
286 assert_not_nil project.errors.on(:parent_id)
286 assert_not_nil project.errors.on(:parent_id)
287 end
287 end
288 end
288 end
289 end
289 end
290
290
291 def test_create_should_preserve_modules_on_validation_failure
291 def test_create_should_preserve_modules_on_validation_failure
292 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
292 with_settings :default_projects_modules => ['issue_tracking', 'repository'] do
293 @request.session[:user_id] = 1
293 @request.session[:user_id] = 1
294 assert_no_difference 'Project.count' do
294 assert_no_difference 'Project.count' do
295 post :create, :project => {
295 post :create, :project => {
296 :name => "blog",
296 :name => "blog",
297 :identifier => "",
297 :identifier => "",
298 :enabled_module_names => %w(issue_tracking news)
298 :enabled_module_names => %w(issue_tracking news)
299 }
299 }
300 end
300 end
301 assert_response :success
301 assert_response :success
302 project = assigns(:project)
302 project = assigns(:project)
303 assert_equal %w(issue_tracking news), project.enabled_module_names.sort
303 assert_equal %w(issue_tracking news), project.enabled_module_names.sort
304 end
304 end
305 end
305 end
306
306
307 def test_create_should_not_accept_get
307 def test_create_should_not_accept_get
308 @request.session[:user_id] = 1
308 @request.session[:user_id] = 1
309 get :create
309 get :create
310 assert_response :method_not_allowed
310 assert_response :method_not_allowed
311 end
311 end
312
312
313 def test_show_by_id
313 def test_show_by_id
314 get :show, :id => 1
314 get :show, :id => 1
315 assert_response :success
315 assert_response :success
316 assert_template 'show'
316 assert_template 'show'
317 assert_not_nil assigns(:project)
317 assert_not_nil assigns(:project)
318 end
318 end
319
319
320 def test_show_by_identifier
320 def test_show_by_identifier
321 get :show, :id => 'ecookbook'
321 get :show, :id => 'ecookbook'
322 assert_response :success
322 assert_response :success
323 assert_template 'show'
323 assert_template 'show'
324 assert_not_nil assigns(:project)
324 assert_not_nil assigns(:project)
325 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
325 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
326
326
327 assert_tag 'li', :content => /Development status/
327 assert_tag 'li', :content => /Development status/
328 end
328 end
329
329
330 def test_show_should_not_display_hidden_custom_fields
330 def test_show_should_not_display_hidden_custom_fields
331 ProjectCustomField.find_by_name('Development status').update_attribute :visible, false
331 ProjectCustomField.find_by_name('Development status').update_attribute :visible, false
332 get :show, :id => 'ecookbook'
332 get :show, :id => 'ecookbook'
333 assert_response :success
333 assert_response :success
334 assert_template 'show'
334 assert_template 'show'
335 assert_not_nil assigns(:project)
335 assert_not_nil assigns(:project)
336
336
337 assert_no_tag 'li', :content => /Development status/
337 assert_no_tag 'li', :content => /Development status/
338 end
338 end
339
339
340 def test_show_should_not_fail_when_custom_values_are_nil
340 def test_show_should_not_fail_when_custom_values_are_nil
341 project = Project.find_by_identifier('ecookbook')
341 project = Project.find_by_identifier('ecookbook')
342 project.custom_values.first.update_attribute(:value, nil)
342 project.custom_values.first.update_attribute(:value, nil)
343 get :show, :id => 'ecookbook'
343 get :show, :id => 'ecookbook'
344 assert_response :success
344 assert_response :success
345 assert_template 'show'
345 assert_template 'show'
346 assert_not_nil assigns(:project)
346 assert_not_nil assigns(:project)
347 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
347 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
348 end
348 end
349
349
350 def show_archived_project_should_be_denied
350 def show_archived_project_should_be_denied
351 project = Project.find_by_identifier('ecookbook')
351 project = Project.find_by_identifier('ecookbook')
352 project.archive!
352 project.archive!
353
353
354 get :show, :id => 'ecookbook'
354 get :show, :id => 'ecookbook'
355 assert_response 403
355 assert_response 403
356 assert_nil assigns(:project)
356 assert_nil assigns(:project)
357 assert_tag :tag => 'p', :content => /archived/
357 assert_tag :tag => 'p', :content => /archived/
358 end
358 end
359
359
360 def test_private_subprojects_hidden
360 def test_private_subprojects_hidden
361 get :show, :id => 'ecookbook'
361 get :show, :id => 'ecookbook'
362 assert_response :success
362 assert_response :success
363 assert_template 'show'
363 assert_template 'show'
364 assert_no_tag :tag => 'a', :content => /Private child/
364 assert_no_tag :tag => 'a', :content => /Private child/
365 end
365 end
366
366
367 def test_private_subprojects_visible
367 def test_private_subprojects_visible
368 @request.session[:user_id] = 2 # manager who is a member of the private subproject
368 @request.session[:user_id] = 2 # manager who is a member of the private subproject
369 get :show, :id => 'ecookbook'
369 get :show, :id => 'ecookbook'
370 assert_response :success
370 assert_response :success
371 assert_template 'show'
371 assert_template 'show'
372 assert_tag :tag => 'a', :content => /Private child/
372 assert_tag :tag => 'a', :content => /Private child/
373 end
373 end
374
374
375 def test_settings
375 def test_settings
376 @request.session[:user_id] = 2 # manager
376 @request.session[:user_id] = 2 # manager
377 get :settings, :id => 1
377 get :settings, :id => 1
378 assert_response :success
378 assert_response :success
379 assert_template 'settings'
379 assert_template 'settings'
380 end
380 end
381
381
382 def test_update
382 def test_update
383 @request.session[:user_id] = 2 # manager
383 @request.session[:user_id] = 2 # manager
384 post :update, :id => 1, :project => {:name => 'Test changed name',
384 post :update, :id => 1, :project => {:name => 'Test changed name',
385 :issue_custom_field_ids => ['']}
385 :issue_custom_field_ids => ['']}
386 assert_redirected_to '/projects/ecookbook/settings'
386 assert_redirected_to '/projects/ecookbook/settings'
387 project = Project.find(1)
387 project = Project.find(1)
388 assert_equal 'Test changed name', project.name
388 assert_equal 'Test changed name', project.name
389 end
389 end
390
390
391 def test_modules
391 def test_modules
392 @request.session[:user_id] = 2
392 @request.session[:user_id] = 2
393 Project.find(1).enabled_module_names = ['issue_tracking', 'news']
393 Project.find(1).enabled_module_names = ['issue_tracking', 'news']
394
394
395 post :modules, :id => 1, :enabled_module_names => ['issue_tracking', 'repository', 'documents']
395 post :modules, :id => 1, :enabled_module_names => ['issue_tracking', 'repository', 'documents']
396 assert_redirected_to '/projects/ecookbook/settings/modules'
396 assert_redirected_to '/projects/ecookbook/settings/modules'
397 assert_equal ['documents', 'issue_tracking', 'repository'], Project.find(1).enabled_module_names.sort
397 assert_equal ['documents', 'issue_tracking', 'repository'], Project.find(1).enabled_module_names.sort
398 end
398 end
399
399
400 def test_modules_should_not_allow_get
400 def test_modules_should_not_allow_get
401 @request.session[:user_id] = 1
401 @request.session[:user_id] = 1
402 get :modules, :id => 1
402 get :modules, :id => 1
403 assert_response :method_not_allowed
403 assert_response :method_not_allowed
404 end
404 end
405
405
406 def test_get_destroy
406 def test_get_destroy
407 @request.session[:user_id] = 1 # admin
407 @request.session[:user_id] = 1 # admin
408 get :destroy, :id => 1
408 get :destroy, :id => 1
409 assert_response :success
409 assert_response :success
410 assert_template 'destroy'
410 assert_template 'destroy'
411 assert_not_nil Project.find_by_id(1)
411 assert_not_nil Project.find_by_id(1)
412 end
412 end
413
413
414 def test_post_destroy
414 def test_post_destroy
415 @request.session[:user_id] = 1 # admin
415 @request.session[:user_id] = 1 # admin
416 post :destroy, :id => 1, :confirm => 1
416 post :destroy, :id => 1, :confirm => 1
417 assert_redirected_to '/admin/projects'
417 assert_redirected_to '/admin/projects'
418 assert_nil Project.find_by_id(1)
418 assert_nil Project.find_by_id(1)
419 end
419 end
420
420
421 def test_archive
421 def test_archive
422 @request.session[:user_id] = 1 # admin
422 @request.session[:user_id] = 1 # admin
423 post :archive, :id => 1
423 post :archive, :id => 1
424 assert_redirected_to '/admin/projects'
424 assert_redirected_to '/admin/projects'
425 assert !Project.find(1).active?
425 assert !Project.find(1).active?
426 end
426 end
427
427
428 def test_unarchive
428 def test_unarchive
429 @request.session[:user_id] = 1 # admin
429 @request.session[:user_id] = 1 # admin
430 Project.find(1).archive
430 Project.find(1).archive
431 post :unarchive, :id => 1
431 post :unarchive, :id => 1
432 assert_redirected_to '/admin/projects'
432 assert_redirected_to '/admin/projects'
433 assert Project.find(1).active?
433 assert Project.find(1).active?
434 end
434 end
435
435
436 def test_project_breadcrumbs_should_be_limited_to_3_ancestors
436 def test_project_breadcrumbs_should_be_limited_to_3_ancestors
437 CustomField.delete_all
437 CustomField.delete_all
438 parent = nil
438 parent = nil
439 6.times do |i|
439 6.times do |i|
440 p = Project.create!(:name => "Breadcrumbs #{i}", :identifier => "breadcrumbs-#{i}")
440 p = Project.create!(:name => "Breadcrumbs #{i}", :identifier => "breadcrumbs-#{i}")
441 p.set_parent!(parent)
441 p.set_parent!(parent)
442 get :show, :id => p
442 get :show, :id => p
443 assert_tag :h1, :parent => { :attributes => {:id => 'header'}},
443 assert_tag :h1, :parent => { :attributes => {:id => 'header'}},
444 :children => { :count => [i, 3].min,
444 :children => { :count => [i, 3].min,
445 :only => { :tag => 'a' } }
445 :only => { :tag => 'a' } }
446
446
447 parent = p
447 parent = p
448 end
448 end
449 end
449 end
450
450
451 def test_copy_with_project
451 def test_copy_with_project
452 @request.session[:user_id] = 1 # admin
452 @request.session[:user_id] = 1 # admin
453 get :copy, :id => 1
453 get :copy, :id => 1
454 assert_response :success
454 assert_response :success
455 assert_template 'copy'
455 assert_template 'copy'
456 assert assigns(:project)
456 assert assigns(:project)
457 assert_equal Project.find(1).description, assigns(:project).description
457 assert_equal Project.find(1).description, assigns(:project).description
458 assert_nil assigns(:project).id
458 assert_nil assigns(:project).id
459 end
459 end
460
460
461 def test_copy_without_project
461 def test_copy_without_project
462 @request.session[:user_id] = 1 # admin
462 @request.session[:user_id] = 1 # admin
463 get :copy
463 get :copy
464 assert_response :redirect
464 assert_response :redirect
465 assert_redirected_to :controller => 'admin', :action => 'projects'
465 assert_redirected_to :controller => 'admin', :action => 'projects'
466 end
466 end
467
467
468 context "POST :copy" do
468 context "POST :copy" do
469 should "TODO: test the rest of the method"
469 should "TODO: test the rest of the method"
470
470
471 should "redirect to the project settings when successful" do
471 should "redirect to the project settings when successful" do
472 @request.session[:user_id] = 1 # admin
472 @request.session[:user_id] = 1 # admin
473 post :copy, :id => 1, :project => {:name => 'Copy', :identifier => 'unique-copy'}
473 post :copy, :id => 1, :project => {:name => 'Copy', :identifier => 'unique-copy'}
474 assert_response :redirect
474 assert_response :redirect
475 assert_redirected_to :controller => 'projects', :action => 'settings', :id => 'unique-copy'
475 assert_redirected_to :controller => 'projects', :action => 'settings', :id => 'unique-copy'
476 end
476 end
477 end
477 end
478
478
479 def test_jump_should_redirect_to_active_tab
479 def test_jump_should_redirect_to_active_tab
480 get :show, :id => 1, :jump => 'issues'
480 get :show, :id => 1, :jump => 'issues'
481 assert_redirected_to '/projects/ecookbook/issues'
481 assert_redirected_to '/projects/ecookbook/issues'
482 end
482 end
483
483
484 def test_jump_should_not_redirect_to_inactive_tab
484 def test_jump_should_not_redirect_to_inactive_tab
485 get :show, :id => 3, :jump => 'documents'
485 get :show, :id => 3, :jump => 'documents'
486 assert_response :success
486 assert_response :success
487 assert_template 'show'
487 assert_template 'show'
488 end
488 end
489
489
490 def test_jump_should_not_redirect_to_unknown_tab
490 def test_jump_should_not_redirect_to_unknown_tab
491 get :show, :id => 3, :jump => 'foobar'
491 get :show, :id => 3, :jump => 'foobar'
492 assert_response :success
492 assert_response :success
493 assert_template 'show'
493 assert_template 'show'
494 end
494 end
495
495
496 # A hook that is manually registered later
496 # A hook that is manually registered later
497 class ProjectBasedTemplate < Redmine::Hook::ViewListener
497 class ProjectBasedTemplate < Redmine::Hook::ViewListener
498 def view_layouts_base_html_head(context)
498 def view_layouts_base_html_head(context)
499 # Adds a project stylesheet
499 # Adds a project stylesheet
500 stylesheet_link_tag(context[:project].identifier) if context[:project]
500 stylesheet_link_tag(context[:project].identifier) if context[:project]
501 end
501 end
502 end
502 end
503 # Don't use this hook now
503 # Don't use this hook now
504 Redmine::Hook.clear_listeners
504 Redmine::Hook.clear_listeners
505
505
506 def test_hook_response
506 def test_hook_response
507 Redmine::Hook.add_listener(ProjectBasedTemplate)
507 Redmine::Hook.add_listener(ProjectBasedTemplate)
508 get :show, :id => 1
508 get :show, :id => 1
509 assert_tag :tag => 'link', :attributes => {:href => '/stylesheets/ecookbook.css'},
509 assert_tag :tag => 'link', :attributes => {:href => '/stylesheets/ecookbook.css'},
510 :parent => {:tag => 'head'}
510 :parent => {:tag => 'head'}
511
511
512 Redmine::Hook.clear_listeners
512 Redmine::Hook.clear_listeners
513 end
513 end
514 end
514 end
General Comments 0
You need to be logged in to leave comments. Login now