##// END OF EJS Templates
Jump to the current tab when using the project quick-jump combo (#2364)....
Jean-Philippe Lang -
r2208:2355324d73e2
parent child
Show More
@@ -1,293 +1,298
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class ProjectsController < ApplicationController
19 19 menu_item :overview
20 20 menu_item :activity, :only => :activity
21 21 menu_item :roadmap, :only => :roadmap
22 22 menu_item :files, :only => [:list_files, :add_file]
23 23 menu_item :settings, :only => :settings
24 24 menu_item :issues, :only => [:changelog]
25 25
26 26 before_filter :find_project, :except => [ :index, :list, :add, :activity ]
27 27 before_filter :find_optional_project, :only => :activity
28 28 before_filter :authorize, :except => [ :index, :list, :add, :archive, :unarchive, :destroy, :activity ]
29 29 before_filter :require_admin, :only => [ :add, :archive, :unarchive, :destroy ]
30 30 accept_key_auth :activity
31 31
32 32 helper :sort
33 33 include SortHelper
34 34 helper :custom_fields
35 35 include CustomFieldsHelper
36 36 helper :ifpdf
37 37 include IfpdfHelper
38 38 helper :issues
39 39 helper IssuesHelper
40 40 helper :queries
41 41 include QueriesHelper
42 42 helper :repositories
43 43 include RepositoriesHelper
44 44 include ProjectsHelper
45 45
46 46 # Lists visible projects
47 47 def index
48 48 projects = Project.find :all,
49 49 :conditions => Project.visible_by(User.current),
50 50 :include => :parent
51 51 respond_to do |format|
52 52 format.html {
53 53 @project_tree = projects.group_by {|p| p.parent || p}
54 54 @project_tree.keys.each {|p| @project_tree[p] -= [p]}
55 55 }
56 56 format.atom {
57 57 render_feed(projects.sort_by(&:created_on).reverse.slice(0, Setting.feeds_limit.to_i),
58 58 :title => "#{Setting.app_title}: #{l(:label_project_latest)}")
59 59 }
60 60 end
61 61 end
62 62
63 63 # Add a new project
64 64 def add
65 65 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
66 66 @trackers = Tracker.all
67 67 @root_projects = Project.find(:all,
68 68 :conditions => "parent_id IS NULL AND status = #{Project::STATUS_ACTIVE}",
69 69 :order => 'name')
70 70 @project = Project.new(params[:project])
71 71 if request.get?
72 72 @project.identifier = Project.next_identifier if Setting.sequential_project_identifiers?
73 73 @project.trackers = Tracker.all
74 74 @project.is_public = Setting.default_projects_public?
75 75 @project.enabled_module_names = Redmine::AccessControl.available_project_modules
76 76 else
77 77 @project.enabled_module_names = params[:enabled_modules]
78 78 if @project.save
79 79 flash[:notice] = l(:notice_successful_create)
80 80 redirect_to :controller => 'admin', :action => 'projects'
81 81 end
82 82 end
83 83 end
84 84
85 85 # Show @project
86 86 def show
87 if params[:jump]
88 # try to redirect to the requested menu item
89 redirect_to_project_menu_item(@project, params[:jump]) && return
90 end
91
87 92 @members_by_role = @project.members.find(:all, :include => [:user, :role], :order => 'position').group_by {|m| m.role}
88 93 @subprojects = @project.children.find(:all, :conditions => Project.visible_by(User.current))
89 94 @news = @project.news.find(:all, :limit => 5, :include => [ :author, :project ], :order => "#{News.table_name}.created_on DESC")
90 95 @trackers = @project.rolled_up_trackers
91 96
92 97 cond = @project.project_condition(Setting.display_subprojects_issues?)
93 98 Issue.visible_by(User.current) do
94 99 @open_issues_by_tracker = Issue.count(:group => :tracker,
95 100 :include => [:project, :status, :tracker],
96 101 :conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
97 102 @total_issues_by_tracker = Issue.count(:group => :tracker,
98 103 :include => [:project, :status, :tracker],
99 104 :conditions => cond)
100 105 end
101 106 TimeEntry.visible_by(User.current) do
102 107 @total_hours = TimeEntry.sum(:hours,
103 108 :include => :project,
104 109 :conditions => cond).to_f
105 110 end
106 111 @key = User.current.rss_key
107 112 end
108 113
109 114 def settings
110 115 @root_projects = Project.find(:all,
111 116 :conditions => ["parent_id IS NULL AND status = #{Project::STATUS_ACTIVE} AND id <> ?", @project.id],
112 117 :order => 'name')
113 118 @issue_custom_fields = IssueCustomField.find(:all, :order => "#{CustomField.table_name}.position")
114 119 @issue_category ||= IssueCategory.new
115 120 @member ||= @project.members.new
116 121 @trackers = Tracker.all
117 122 @repository ||= @project.repository
118 123 @wiki ||= @project.wiki
119 124 end
120 125
121 126 # Edit @project
122 127 def edit
123 128 if request.post?
124 129 @project.attributes = params[:project]
125 130 if @project.save
126 131 flash[:notice] = l(:notice_successful_update)
127 132 redirect_to :action => 'settings', :id => @project
128 133 else
129 134 settings
130 135 render :action => 'settings'
131 136 end
132 137 end
133 138 end
134 139
135 140 def modules
136 141 @project.enabled_module_names = params[:enabled_modules]
137 142 redirect_to :action => 'settings', :id => @project, :tab => 'modules'
138 143 end
139 144
140 145 def archive
141 146 @project.archive if request.post? && @project.active?
142 147 redirect_to :controller => 'admin', :action => 'projects'
143 148 end
144 149
145 150 def unarchive
146 151 @project.unarchive if request.post? && !@project.active?
147 152 redirect_to :controller => 'admin', :action => 'projects'
148 153 end
149 154
150 155 # Delete @project
151 156 def destroy
152 157 @project_to_destroy = @project
153 158 if request.post? and params[:confirm]
154 159 @project_to_destroy.destroy
155 160 redirect_to :controller => 'admin', :action => 'projects'
156 161 end
157 162 # hide project in layout
158 163 @project = nil
159 164 end
160 165
161 166 # Add a new issue category to @project
162 167 def add_issue_category
163 168 @category = @project.issue_categories.build(params[:category])
164 169 if request.post? and @category.save
165 170 respond_to do |format|
166 171 format.html do
167 172 flash[:notice] = l(:notice_successful_create)
168 173 redirect_to :action => 'settings', :tab => 'categories', :id => @project
169 174 end
170 175 format.js do
171 176 # IE doesn't support the replace_html rjs method for select box options
172 177 render(:update) {|page| page.replace "issue_category_id",
173 178 content_tag('select', '<option></option>' + options_from_collection_for_select(@project.issue_categories, 'id', 'name', @category.id), :id => 'issue_category_id', :name => 'issue[category_id]')
174 179 }
175 180 end
176 181 end
177 182 end
178 183 end
179 184
180 185 # Add a new version to @project
181 186 def add_version
182 187 @version = @project.versions.build(params[:version])
183 188 if request.post? and @version.save
184 189 flash[:notice] = l(:notice_successful_create)
185 190 redirect_to :action => 'settings', :tab => 'versions', :id => @project
186 191 end
187 192 end
188 193
189 194 def add_file
190 195 if request.post?
191 196 container = (params[:version_id].blank? ? @project : @project.versions.find_by_id(params[:version_id]))
192 197 attachments = attach_files(container, params[:attachments])
193 198 if !attachments.empty? && Setting.notified_events.include?('file_added')
194 199 Mailer.deliver_attachments_added(attachments)
195 200 end
196 201 redirect_to :controller => 'projects', :action => 'list_files', :id => @project
197 202 return
198 203 end
199 204 @versions = @project.versions.sort
200 205 end
201 206
202 207 def list_files
203 208 sort_init 'filename', 'asc'
204 209 sort_update 'filename' => "#{Attachment.table_name}.filename",
205 210 'created_on' => "#{Attachment.table_name}.created_on",
206 211 'size' => "#{Attachment.table_name}.filesize",
207 212 'downloads' => "#{Attachment.table_name}.downloads"
208 213
209 214 @containers = [ Project.find(@project.id, :include => :attachments, :order => sort_clause)]
210 215 @containers += @project.versions.find(:all, :include => :attachments, :order => sort_clause).sort.reverse
211 216 render :layout => !request.xhr?
212 217 end
213 218
214 219 # Show changelog for @project
215 220 def changelog
216 221 @trackers = @project.trackers.find(:all, :conditions => ["is_in_chlog=?", true], :order => 'position')
217 222 retrieve_selected_tracker_ids(@trackers)
218 223 @versions = @project.versions.sort
219 224 end
220 225
221 226 def roadmap
222 227 @trackers = @project.trackers.find(:all, :conditions => ["is_in_roadmap=?", true])
223 228 retrieve_selected_tracker_ids(@trackers)
224 229 @versions = @project.versions.sort
225 230 @versions = @versions.select {|v| !v.completed? } unless params[:completed]
226 231 end
227 232
228 233 def activity
229 234 @days = Setting.activity_days_default.to_i
230 235
231 236 if params[:from]
232 237 begin; @date_to = params[:from].to_date + 1; rescue; end
233 238 end
234 239
235 240 @date_to ||= Date.today + 1
236 241 @date_from = @date_to - @days
237 242 @with_subprojects = params[:with_subprojects].nil? ? Setting.display_subprojects_issues? : (params[:with_subprojects] == '1')
238 243 @author = (params[:user_id].blank? ? nil : User.active.find(params[:user_id]))
239 244
240 245 @activity = Redmine::Activity::Fetcher.new(User.current, :project => @project,
241 246 :with_subprojects => @with_subprojects,
242 247 :author => @author)
243 248 @activity.scope_select {|t| !params["show_#{t}"].nil?}
244 249 @activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
245 250
246 251 events = @activity.events(@date_from, @date_to)
247 252
248 253 respond_to do |format|
249 254 format.html {
250 255 @events_by_day = events.group_by(&:event_date)
251 256 render :layout => false if request.xhr?
252 257 }
253 258 format.atom {
254 259 title = l(:label_activity)
255 260 if @author
256 261 title = @author.name
257 262 elsif @activity.scope.size == 1
258 263 title = l("label_#{@activity.scope.first.singularize}_plural")
259 264 end
260 265 render_feed(events, :title => "#{@project || Setting.app_title}: #{title}")
261 266 }
262 267 end
263 268
264 269 rescue ActiveRecord::RecordNotFound
265 270 render_404
266 271 end
267 272
268 273 private
269 274 # Find project of id params[:id]
270 275 # if not found, redirect to project list
271 276 # Used as a before_filter
272 277 def find_project
273 278 @project = Project.find(params[:id])
274 279 rescue ActiveRecord::RecordNotFound
275 280 render_404
276 281 end
277 282
278 283 def find_optional_project
279 284 return true unless params[:id]
280 285 @project = Project.find(params[:id])
281 286 authorize
282 287 rescue ActiveRecord::RecordNotFound
283 288 render_404
284 289 end
285 290
286 291 def retrieve_selected_tracker_ids(selectable_trackers)
287 292 if ids = params[:tracker_ids]
288 293 @selected_tracker_ids = (ids.is_a? Array) ? ids.collect { |id| id.to_i.to_s } : ids.split('/').collect { |id| id.to_i.to_s }
289 294 else
290 295 @selected_tracker_ids = selectable_trackers.collect {|t| t.id.to_s }
291 296 end
292 297 end
293 298 end
@@ -1,12 +1,12
1 1 <% user_projects_by_root = User.current.projects.find(:all, :include => :parent).group_by(&:root) %>
2 2 <select onchange="if (this.value != '') { window.location = this.value; }">
3 3 <option selected="selected"><%= l(:label_jump_to_a_project) %></option>
4 4 <option disabled="disabled">---</option>
5 5 <% user_projects_by_root.keys.sort.each do |root| %>
6 <%= content_tag('option', h(root.name), :value => url_for(:controller => 'projects', :action => 'show', :id => root)) %>
6 <%= content_tag('option', h(root.name), :value => url_for(:controller => 'projects', :action => 'show', :id => root, :jump => current_menu_item)) %>
7 7 <% user_projects_by_root[root].sort.each do |project| %>
8 8 <% next if project == root %>
9 <%= content_tag('option', ('&#187; ' + h(project.name)), :value => url_for(:controller => 'projects', :action => 'show', :id => project)) %>
9 <%= content_tag('option', ('&#187; ' + h(project.name)), :value => url_for(:controller => 'projects', :action => 'show', :id => project, :jump => current_menu_item)) %>
10 10 <% end %>
11 11 <% end %>
12 12 </select>
@@ -1,208 +1,219
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 'gloc'
19 19
20 20 module Redmine
21 21 module MenuManager
22 22 module MenuController
23 23 def self.included(base)
24 24 base.extend(ClassMethods)
25 25 end
26 26
27 27 module ClassMethods
28 28 @@menu_items = Hash.new {|hash, key| hash[key] = {:default => key, :actions => {}}}
29 29 mattr_accessor :menu_items
30 30
31 31 # Set the menu item name for a controller or specific actions
32 32 # Examples:
33 33 # * menu_item :tickets # => sets the menu name to :tickets for the whole controller
34 34 # * menu_item :tickets, :only => :list # => sets the menu name to :tickets for the 'list' action only
35 35 # * menu_item :tickets, :only => [:list, :show] # => sets the menu name to :tickets for 2 actions only
36 36 #
37 37 # The default menu item name for a controller is controller_name by default
38 38 # Eg. the default menu item name for ProjectsController is :projects
39 39 def menu_item(id, options = {})
40 40 if actions = options[:only]
41 41 actions = [] << actions unless actions.is_a?(Array)
42 42 actions.each {|a| menu_items[controller_name.to_sym][:actions][a.to_sym] = id}
43 43 else
44 44 menu_items[controller_name.to_sym][:default] = id
45 45 end
46 46 end
47 47 end
48 48
49 49 def menu_items
50 50 self.class.menu_items
51 51 end
52 52
53 53 # Returns the menu item name according to the current action
54 54 def current_menu_item
55 menu_items[controller_name.to_sym][:actions][action_name.to_sym] ||
56 menu_items[controller_name.to_sym][:default]
55 @current_menu_item ||= menu_items[controller_name.to_sym][:actions][action_name.to_sym] ||
56 menu_items[controller_name.to_sym][:default]
57 end
58
59 # Redirects user to the menu item of the given project
60 # Returns false if user is not authorized
61 def redirect_to_project_menu_item(project, name)
62 item = Redmine::MenuManager.items(:project_menu).detect {|i| i.name.to_s == name.to_s}
63 if item && User.current.allowed_to?(item.url, project) && (item.condition.nil? || item.condition.call(project))
64 redirect_to({item.param => project}.merge(item.url))
65 return true
66 end
67 false
57 68 end
58 69 end
59 70
60 71 module MenuHelper
61 72 # Returns the current menu item name
62 73 def current_menu_item
63 74 @controller.current_menu_item
64 75 end
65 76
66 77 # Renders the application main menu
67 78 def render_main_menu(project)
68 79 render_menu((project && !project.new_record?) ? :project_menu : :application_menu, project)
69 80 end
70 81
71 82 def render_menu(menu, project=nil)
72 83 links = []
73 84 menu_items_for(menu, project) do |item, caption, url, selected|
74 85 links << content_tag('li',
75 86 link_to(h(caption), url, item.html_options(:selected => selected)))
76 87 end
77 88 links.empty? ? nil : content_tag('ul', links.join("\n"))
78 89 end
79 90
80 91 def menu_items_for(menu, project=nil)
81 92 items = []
82 93 Redmine::MenuManager.allowed_items(menu, User.current, project).each do |item|
83 94 unless item.condition && !item.condition.call(project)
84 95 url = case item.url
85 96 when Hash
86 97 project.nil? ? item.url : {item.param => project}.merge(item.url)
87 98 when Symbol
88 99 send(item.url)
89 100 else
90 101 item.url
91 102 end
92 103 caption = item.caption(project)
93 104 caption = l(caption) if caption.is_a?(Symbol)
94 105 if block_given?
95 106 yield item, caption, url, (current_menu_item == item.name)
96 107 else
97 108 items << [item, caption, url, (current_menu_item == item.name)]
98 109 end
99 110 end
100 111 end
101 112 return block_given? ? nil : items
102 113 end
103 114 end
104 115
105 116 class << self
106 117 def map(menu_name)
107 118 @items ||= {}
108 119 mapper = Mapper.new(menu_name.to_sym, @items)
109 120 if block_given?
110 121 yield mapper
111 122 else
112 123 mapper
113 124 end
114 125 end
115 126
116 127 def items(menu_name)
117 128 @items[menu_name.to_sym] || []
118 129 end
119 130
120 131 def allowed_items(menu_name, user, project)
121 132 project ? items(menu_name).select {|item| user && user.allowed_to?(item.url, project)} : items(menu_name)
122 133 end
123 134 end
124 135
125 136 class Mapper
126 137 def initialize(menu, items)
127 138 items[menu] ||= []
128 139 @menu = menu
129 140 @menu_items = items[menu]
130 141 end
131 142
132 143 @@last_items_count = Hash.new {|h,k| h[k] = 0}
133 144
134 145 # Adds an item at the end of the menu. Available options:
135 146 # * param: the parameter name that is used for the project id (default is :id)
136 147 # * if: a Proc that is called before rendering the item, the item is displayed only if it returns true
137 148 # * caption that can be:
138 149 # * a localized string Symbol
139 150 # * a String
140 151 # * a Proc that can take the project as argument
141 152 # * before, after: specify where the menu item should be inserted (eg. :after => :activity)
142 153 # * last: menu item will stay at the end (eg. :last => true)
143 154 # * html_options: a hash of html options that are passed to link_to
144 155 def push(name, url, options={})
145 156 options = options.dup
146 157
147 158 # menu item position
148 159 if before = options.delete(:before)
149 160 position = @menu_items.collect(&:name).index(before)
150 161 elsif after = options.delete(:after)
151 162 position = @menu_items.collect(&:name).index(after)
152 163 position += 1 unless position.nil?
153 164 elsif options.delete(:last)
154 165 position = @menu_items.size
155 166 @@last_items_count[@menu] += 1
156 167 end
157 168 # default position
158 169 position ||= @menu_items.size - @@last_items_count[@menu]
159 170
160 171 @menu_items.insert(position, MenuItem.new(name, url, options))
161 172 end
162 173
163 174 # Removes a menu item
164 175 def delete(name)
165 176 @menu_items.delete_if {|i| i.name == name}
166 177 end
167 178 end
168 179
169 180 class MenuItem
170 181 include GLoc
171 182 attr_reader :name, :url, :param, :condition
172 183
173 184 def initialize(name, url, options)
174 185 raise "Invalid option :if for menu item '#{name}'" if options[:if] && !options[:if].respond_to?(:call)
175 186 raise "Invalid option :html for menu item '#{name}'" if options[:html] && !options[:html].is_a?(Hash)
176 187 @name = name
177 188 @url = url
178 189 @condition = options[:if]
179 190 @param = options[:param] || :id
180 191 @caption = options[:caption]
181 192 @html_options = options[:html] || {}
182 193 # Adds a unique class to each menu item based on its name
183 194 @html_options[:class] = [@html_options[:class], @name.to_s.dasherize].compact.join(' ')
184 195 end
185 196
186 197 def caption(project=nil)
187 198 if @caption.is_a?(Proc)
188 199 c = @caption.call(project).to_s
189 200 c = @name.to_s.humanize if c.blank?
190 201 c
191 202 else
192 203 # check if localized string exists on first render (after GLoc strings are loaded)
193 204 @caption_key ||= (@caption || (l_has_string?("label_#{@name}".to_sym) ? "label_#{@name}".to_sym : @name.to_s.humanize))
194 205 end
195 206 end
196 207
197 208 def html_options(options={})
198 209 if options[:selected]
199 210 o = @html_options.dup
200 211 o[:class] += ' selected'
201 212 o
202 213 else
203 214 @html_options
204 215 end
205 216 end
206 217 end
207 218 end
208 219 end
@@ -1,340 +1,357
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2008 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 'projects_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class ProjectsController; def rescue_action(e) raise e end; end
23 23
24 24 class ProjectsControllerTest < Test::Unit::TestCase
25 25 fixtures :projects, :versions, :users, :roles, :members, :issues, :journals, :journal_details,
26 26 :trackers, :projects_trackers, :issue_statuses, :enabled_modules, :enumerations, :boards, :messages,
27 27 :attachments
28 28
29 29 def setup
30 30 @controller = ProjectsController.new
31 31 @request = ActionController::TestRequest.new
32 32 @response = ActionController::TestResponse.new
33 33 @request.session[:user_id] = nil
34 34 Setting.default_language = 'en'
35 35 end
36 36
37 37 def test_index
38 38 get :index
39 39 assert_response :success
40 40 assert_template 'index'
41 41 assert_not_nil assigns(:project_tree)
42 42 # Root project as hash key
43 43 assert assigns(:project_tree).keys.include?(Project.find(1))
44 44 # Subproject in corresponding value
45 45 assert assigns(:project_tree)[Project.find(1)].include?(Project.find(3))
46 46 end
47 47
48 48 def test_index_atom
49 49 get :index, :format => 'atom'
50 50 assert_response :success
51 51 assert_template 'common/feed.atom.rxml'
52 52 assert_select 'feed>title', :text => 'Redmine: Latest projects'
53 53 assert_select 'feed>entry', :count => Project.count(:conditions => Project.visible_by(User.current))
54 54 end
55 55
56 56 def test_show_by_id
57 57 get :show, :id => 1
58 58 assert_response :success
59 59 assert_template 'show'
60 60 assert_not_nil assigns(:project)
61 61 end
62 62
63 63 def test_show_by_identifier
64 64 get :show, :id => 'ecookbook'
65 65 assert_response :success
66 66 assert_template 'show'
67 67 assert_not_nil assigns(:project)
68 68 assert_equal Project.find_by_identifier('ecookbook'), assigns(:project)
69 69 end
70 70
71 71 def test_private_subprojects_hidden
72 72 get :show, :id => 'ecookbook'
73 73 assert_response :success
74 74 assert_template 'show'
75 75 assert_no_tag :tag => 'a', :content => /Private child/
76 76 end
77 77
78 78 def test_private_subprojects_visible
79 79 @request.session[:user_id] = 2 # manager who is a member of the private subproject
80 80 get :show, :id => 'ecookbook'
81 81 assert_response :success
82 82 assert_template 'show'
83 83 assert_tag :tag => 'a', :content => /Private child/
84 84 end
85 85
86 86 def test_settings
87 87 @request.session[:user_id] = 2 # manager
88 88 get :settings, :id => 1
89 89 assert_response :success
90 90 assert_template 'settings'
91 91 end
92 92
93 93 def test_edit
94 94 @request.session[:user_id] = 2 # manager
95 95 post :edit, :id => 1, :project => {:name => 'Test changed name',
96 96 :issue_custom_field_ids => ['']}
97 97 assert_redirected_to 'projects/settings/ecookbook'
98 98 project = Project.find(1)
99 99 assert_equal 'Test changed name', project.name
100 100 end
101 101
102 102 def test_get_destroy
103 103 @request.session[:user_id] = 1 # admin
104 104 get :destroy, :id => 1
105 105 assert_response :success
106 106 assert_template 'destroy'
107 107 assert_not_nil Project.find_by_id(1)
108 108 end
109 109
110 110 def test_post_destroy
111 111 @request.session[:user_id] = 1 # admin
112 112 post :destroy, :id => 1, :confirm => 1
113 113 assert_redirected_to 'admin/projects'
114 114 assert_nil Project.find_by_id(1)
115 115 end
116 116
117 117 def test_add_file
118 118 set_tmp_attachments_directory
119 119 @request.session[:user_id] = 2
120 120 Setting.notified_events = ['file_added']
121 121 ActionMailer::Base.deliveries.clear
122 122
123 123 assert_difference 'Attachment.count' do
124 124 post :add_file, :id => 1, :version_id => '',
125 125 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain')}}
126 126 end
127 127 assert_redirected_to 'projects/list_files/ecookbook'
128 128 a = Attachment.find(:first, :order => 'created_on DESC')
129 129 assert_equal 'testfile.txt', a.filename
130 130 assert_equal Project.find(1), a.container
131 131
132 132 mail = ActionMailer::Base.deliveries.last
133 133 assert_kind_of TMail::Mail, mail
134 134 assert_equal "[eCookbook] New file", mail.subject
135 135 assert mail.body.include?('testfile.txt')
136 136 end
137 137
138 138 def test_add_version_file
139 139 set_tmp_attachments_directory
140 140 @request.session[:user_id] = 2
141 141 Setting.notified_events = ['file_added']
142 142
143 143 assert_difference 'Attachment.count' do
144 144 post :add_file, :id => 1, :version_id => '2',
145 145 :attachments => {'1' => {'file' => test_uploaded_file('testfile.txt', 'text/plain')}}
146 146 end
147 147 assert_redirected_to 'projects/list_files/ecookbook'
148 148 a = Attachment.find(:first, :order => 'created_on DESC')
149 149 assert_equal 'testfile.txt', a.filename
150 150 assert_equal Version.find(2), a.container
151 151 end
152 152
153 153 def test_list_files
154 154 get :list_files, :id => 1
155 155 assert_response :success
156 156 assert_template 'list_files'
157 157 assert_not_nil assigns(:containers)
158 158
159 159 # file attached to the project
160 160 assert_tag :a, :content => 'project_file.zip',
161 161 :attributes => { :href => '/attachments/download/8/project_file.zip' }
162 162
163 163 # file attached to a project's version
164 164 assert_tag :a, :content => 'version_file.zip',
165 165 :attributes => { :href => '/attachments/download/9/version_file.zip' }
166 166 end
167 167
168 168 def test_changelog
169 169 get :changelog, :id => 1
170 170 assert_response :success
171 171 assert_template 'changelog'
172 172 assert_not_nil assigns(:versions)
173 173 end
174 174
175 175 def test_roadmap
176 176 get :roadmap, :id => 1
177 177 assert_response :success
178 178 assert_template 'roadmap'
179 179 assert_not_nil assigns(:versions)
180 180 # Version with no date set appears
181 181 assert assigns(:versions).include?(Version.find(3))
182 182 # Completed version doesn't appear
183 183 assert !assigns(:versions).include?(Version.find(1))
184 184 end
185 185
186 186 def test_roadmap_with_completed_versions
187 187 get :roadmap, :id => 1, :completed => 1
188 188 assert_response :success
189 189 assert_template 'roadmap'
190 190 assert_not_nil assigns(:versions)
191 191 # Version with no date set appears
192 192 assert assigns(:versions).include?(Version.find(3))
193 193 # Completed version appears
194 194 assert assigns(:versions).include?(Version.find(1))
195 195 end
196 196
197 197 def test_project_activity
198 198 get :activity, :id => 1, :with_subprojects => 0
199 199 assert_response :success
200 200 assert_template 'activity'
201 201 assert_not_nil assigns(:events_by_day)
202 202
203 203 assert_tag :tag => "h3",
204 204 :content => /#{2.days.ago.to_date.day}/,
205 205 :sibling => { :tag => "dl",
206 206 :child => { :tag => "dt",
207 207 :attributes => { :class => /issue-edit/ },
208 208 :child => { :tag => "a",
209 209 :content => /(#{IssueStatus.find(2).name})/,
210 210 }
211 211 }
212 212 }
213 213 end
214 214
215 215 def test_previous_project_activity
216 216 get :activity, :id => 1, :from => 3.days.ago.to_date
217 217 assert_response :success
218 218 assert_template 'activity'
219 219 assert_not_nil assigns(:events_by_day)
220 220
221 221 assert_tag :tag => "h3",
222 222 :content => /#{3.day.ago.to_date.day}/,
223 223 :sibling => { :tag => "dl",
224 224 :child => { :tag => "dt",
225 225 :attributes => { :class => /issue/ },
226 226 :child => { :tag => "a",
227 227 :content => /#{Issue.find(1).subject}/,
228 228 }
229 229 }
230 230 }
231 231 end
232 232
233 233 def test_global_activity
234 234 get :activity
235 235 assert_response :success
236 236 assert_template 'activity'
237 237 assert_not_nil assigns(:events_by_day)
238 238
239 239 assert_tag :tag => "h3",
240 240 :content => /#{5.day.ago.to_date.day}/,
241 241 :sibling => { :tag => "dl",
242 242 :child => { :tag => "dt",
243 243 :attributes => { :class => /issue/ },
244 244 :child => { :tag => "a",
245 245 :content => /#{Issue.find(5).subject}/,
246 246 }
247 247 }
248 248 }
249 249 end
250 250
251 251 def test_user_activity
252 252 get :activity, :user_id => 2
253 253 assert_response :success
254 254 assert_template 'activity'
255 255 assert_not_nil assigns(:events_by_day)
256 256
257 257 assert_tag :tag => "h3",
258 258 :content => /#{3.day.ago.to_date.day}/,
259 259 :sibling => { :tag => "dl",
260 260 :child => { :tag => "dt",
261 261 :attributes => { :class => /issue/ },
262 262 :child => { :tag => "a",
263 263 :content => /#{Issue.find(1).subject}/,
264 264 }
265 265 }
266 266 }
267 267 end
268 268
269 269 def test_activity_atom_feed
270 270 get :activity, :format => 'atom'
271 271 assert_response :success
272 272 assert_template 'common/feed.atom.rxml'
273 273 end
274 274
275 275 def test_archive
276 276 @request.session[:user_id] = 1 # admin
277 277 post :archive, :id => 1
278 278 assert_redirected_to 'admin/projects'
279 279 assert !Project.find(1).active?
280 280 end
281 281
282 282 def test_unarchive
283 283 @request.session[:user_id] = 1 # admin
284 284 Project.find(1).archive
285 285 post :unarchive, :id => 1
286 286 assert_redirected_to 'admin/projects'
287 287 assert Project.find(1).active?
288 288 end
289 289
290 def test_jump_should_redirect_to_active_tab
291 get :show, :id => 1, :jump => 'issues'
292 assert_redirected_to 'projects/ecookbook/issues'
293 end
294
295 def test_jump_should_not_redirect_to_inactive_tab
296 get :show, :id => 3, :jump => 'documents'
297 assert_response :success
298 assert_template 'show'
299 end
300
301 def test_jump_should_not_redirect_to_unknown_tab
302 get :show, :id => 3, :jump => 'foobar'
303 assert_response :success
304 assert_template 'show'
305 end
306
290 307 def test_project_menu
291 308 assert_no_difference 'Redmine::MenuManager.items(:project_menu).size' do
292 309 Redmine::MenuManager.map :project_menu do |menu|
293 310 menu.push :foo, { :controller => 'projects', :action => 'show' }, :cation => 'Foo'
294 311 menu.push :bar, { :controller => 'projects', :action => 'show' }, :before => :activity
295 312 menu.push :hello, { :controller => 'projects', :action => 'show' }, :caption => Proc.new {|p| p.name.upcase }, :after => :bar
296 313 end
297 314
298 315 get :show, :id => 1
299 316 assert_tag :div, :attributes => { :id => 'main-menu' },
300 317 :descendant => { :tag => 'li', :child => { :tag => 'a', :content => 'Foo',
301 318 :attributes => { :class => 'foo' } } }
302 319
303 320 assert_tag :div, :attributes => { :id => 'main-menu' },
304 321 :descendant => { :tag => 'li', :child => { :tag => 'a', :content => 'Bar',
305 322 :attributes => { :class => 'bar' } },
306 323 :before => { :tag => 'li', :child => { :tag => 'a', :content => 'ECOOKBOOK' } } }
307 324
308 325 assert_tag :div, :attributes => { :id => 'main-menu' },
309 326 :descendant => { :tag => 'li', :child => { :tag => 'a', :content => 'ECOOKBOOK',
310 327 :attributes => { :class => 'hello' } },
311 328 :before => { :tag => 'li', :child => { :tag => 'a', :content => 'Activity' } } }
312 329
313 330 # Remove the menu items
314 331 Redmine::MenuManager.map :project_menu do |menu|
315 332 menu.delete :foo
316 333 menu.delete :bar
317 334 menu.delete :hello
318 335 end
319 336 end
320 337 end
321 338
322 339 # A hook that is manually registered later
323 340 class ProjectBasedTemplate < Redmine::Hook::ViewListener
324 341 def view_layouts_base_html_head(context)
325 342 # Adds a project stylesheet
326 343 stylesheet_link_tag(context[:project].identifier) if context[:project]
327 344 end
328 345 end
329 346 # Don't use this hook now
330 347 Redmine::Hook.clear_listeners
331 348
332 349 def test_hook_response
333 350 Redmine::Hook.add_listener(ProjectBasedTemplate)
334 351 get :show, :id => 1
335 352 assert_tag :tag => 'link', :attributes => {:href => '/stylesheets/ecookbook.css'},
336 353 :parent => {:tag => 'head'}
337 354
338 355 Redmine::Hook.clear_listeners
339 356 end
340 357 end
General Comments 0
You need to be logged in to leave comments. Login now